android 发送语音功能和ios交互格式aac
android 发送语音功能和ios交互格式aac
import android.content.Context; import android.os.Environment; import android.os.Handler; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.Button; import java.io.File; import java.util.UUID; /***************************************************** * author: wz * email: wangzhong0116@foxmail.com * version: 1.0 * date: 2016/12/6 09:47 * description: 控制录音Button * 1、重写onTouchEvent;(changeState方法、wantToCancel方法、reset方法); * 2、编写AudioDialogManage、并与该类AudioRecorderButton进行整合; * 3、编写AudioManage、并与该类AudioRecorderButton进行整合; *****************************************************/ public class AudioRecorderButton extends Button implements AudioManage.AudioStateListenter {/** * AudioRecorderButton的三个状态 */ private static final int STATE_NORMAL = 1; //默认状态 private static final int STATE_RECORDERING = 2; //录音状态 private static final int STATE_WANT_TO_CALCEL = 3; //取消状态 private int mCurState = STATE_NORMAL; // 当前录音状态 private boolean isRecordering = false; // 是否已经开始录音 private boolean mReady; // 是否触发onLongClick private static final int DISTANCE_Y_CANCEL = 50; private AudioDialogManage audioDialogManage; private AudioManage mAudioManage; // VoAACRecordManager manager; String mRecordFileName; private Handler handler = new Handler() {public void handleMessage(android.os.Message msg) {int v = msg.what; System.out.println("=======" + v); // tv_voice.setText("voice:" + v); }}; /** * 正常录音完成后的回调 */ public interface AudioFinishRecorderListenter {void onFinish(float seconds, String FilePath); }private AudioFinishRecorderListenter mListenter; public void setAudioFinishRecorderListenter(AudioFinishRecorderListenter listenter) {this.mListenter = listenter; }//构造方法 public AudioRecorderButton(Context context) {super(context, null); // TODO Auto-generated constructor stub }public AudioRecorderButton(Context context, AttributeSet attrs) {super(context, attrs); audioDialogManage = new AudioDialogManage(getContext()); final String dir = Environment.getExternalStorageDirectory()+ "/VoiceRecorder/"; // 此处需要判断是否有存储卡(外存) File file1 = new File(dir); // if (!file1.exists()) {file1.mkdirs(); } setOnLongClickListener(new OnLongClickListener() {@Override public boolean onLongClick(View v) {mRecordFileName = dir + UUID.randomUUID().toString() + ".aac"; File file = new File(mRecordFileName); mAudioManage = AudioManage.getInstance(file); mAudioManage.setOnAudioStateListenter(AudioRecorderButton.this); mReady = true; // 真正显示应该在audio end prepared以后 mAudioManage.prepareAudio(); isRecordering = true; mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED); return false; }}); // TODO Auto-generated constructor stub }/* * 复写onTouchEvent */ @Override public boolean onTouchEvent(MotionEvent event) {int action = event.getAction(); //获取当前Action int x = (int) event.getX(); //获取当前的坐标 int y = (int) event.getY(); switch (action) {case MotionEvent.ACTION_DOWN:changeState(STATE_RECORDERING); break; case MotionEvent.ACTION_MOVE:// 已经开始录音状态时,根据X、Y的坐标,判断是否想要取消 if (isRecordering) {if (wantToCancel(x, y)) {changeState(STATE_WANT_TO_CALCEL); } else {changeState(STATE_RECORDERING); }}break; case MotionEvent.ACTION_UP:if (!mReady) { //没有触发onLongClick reset(); return super.onTouchEvent(event); }if (!isRecordering || mTime < 0.7f) { //录音时间过短 audioDialogManage.tooShort(); mAudioManage.cancel(); cancle(); mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1300);// 延迟,1.3秒以后关闭“时间过短对话框” } else if (mCurState == STATE_RECORDERING) { //正常录制结束 audioDialogManage.dimissDialog(); // release mAudioManage.release(); // callbackToAct // 正常录制结束,回调录音时间和录音文件完整路径——在播放的时候需要使用 if (mListenter != null) {mListenter.onFinish(mTime, mRecordFileName); }} else if (mCurState == STATE_WANT_TO_CALCEL) {// cancel audioDialogManage.dimissDialog(); mAudioManage.cancel(); cancle(); }reset(); break; }return super.onTouchEvent(event); }/** * 恢复状态以及一些标志位 */ private void reset() {isRecordering = false; mReady = false; //是否触发onLongClick mTime = 0; changeState(STATE_NORMAL); }private boolean wantToCancel(int x, int y) {// 判断手指的滑动是否超出范围 if (x < 0 || x > getWidth()) {return true; }if (y < -DISTANCE_Y_CANCEL || y > getHeight() + DISTANCE_Y_CANCEL) {return true; }return false; }private void cancle() {if (mRecordFileName != null) {File file = new File(mRecordFileName); file.delete(); //删除录音文件 mRecordFileName = null; }}/** * 改变Button的背景和文本、展示不同状态的录音提示对话框 * * @param state */ private void changeState(int state) {if (mCurState != state) {mCurState = state; switch (state) {case STATE_NORMAL:setBackgroundResource(R.drawable.btn_recorder_normal); setText(R.string.str_recorder_normal); break; case STATE_RECORDERING:setBackgroundResource(R.drawable.btn_recorder_recordering); setText(R.string.str_recorder_recording); if (isRecordering) {// 更新Dialog.recording() audioDialogManage.recording(); }break; case STATE_WANT_TO_CALCEL:setBackgroundResource(R.drawable.btn_recorder_recordering); setText(R.string.str_recorder_want_cancel); // 更新Dialog.wantCancel() audioDialogManage.wantToCancel(); break; }}}@Override public void wellPrepared() {// TODO Auto-generated method stub mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED); }private static final int MSG_AUDIO_PREPARED = 0x110; //准备完全 private static final int MSG_VOICE_CHANGE = 0x111; //声音改变 private static final int MSG_DIALOG_DIMISS = 0x112; //销毁对话框 /** * 接收子线程数据,并用此数据配合主线程更新UI * Handler运行在主线程(UI线程)中,它与子线程通过Message对象传递数据。 * Handler接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,把这些消息放入主线程队列中,配合主线程进行更新UI。 */ private Handler mHandler = new Handler() {public void handleMessage(android.os.Message msg) {switch (msg.what) {case MSG_AUDIO_PREPARED: //216:mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED); audioDialogManage.showRecorderingDialog(); isRecordering = true; //已经在录制,同时开启一个获取音量、并且计时的线程 new Thread(mGetVoiceLevelRunnable).start(); break; case MSG_VOICE_CHANGE: //265:mHandler.sendEmptyMessage(MSG_VOICE_CHANGE); audioDialogManage.updateVoiceLevel(mAudioManage .getVoiceLevel(7)); break; //这里在Handler里面处理DIALOG_DIMISS,是因为想让该对话框显示一段时间,延迟关闭,——详见125行 case MSG_DIALOG_DIMISS: //125:mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1300); audioDialogManage.dimissDialog(); break; }}; }; private float mTime; //开始录音时,计时;(在reset()中置空) /** * 获取音量大小的Runnable */ private Runnable mGetVoiceLevelRunnable = new Runnable() {@Override public void run() {while (isRecordering) {try {Thread.sleep(100); mTime += 0.1f; mHandler.sendEmptyMessage(MSG_VOICE_CHANGE); } catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace(); }}}}; }
下面录音的封装 格式aac格式
voaacencoder.jar
import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; import android.os.Handler; import android.os.SystemClock; import com.sinaapp.bashell.VoAACEncoder; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /***************************************************** * author: wz * email: wangzhong0116@foxmail.com * version: 1.0 * date: 2016/12/6 09:52 * description: *****************************************************/ public class AudioManage {private static AudioManage mInstance; private final String TAG = "VoAAVRecordManager"; private AudioRecord mAudioRecord; private int SAMPLERATE = 8000;// 设置采样率 private int bitRate = 16000;// 设置bit率 private Handler mHandler; private long startTime; private long endTime; private boolean isRecodering; private int SPACE = 300;// 间隔取样时间 private FileOutputStream fos; private int r; private int v; private File file; private AudioManage(File file) {this.file = file; }/** * 回调“准备完毕” * @author wz * */ public interface AudioStateListenter {void wellPrepared(); // prepared完毕 }public AudioStateListenter mListenter; public void setOnAudioStateListenter(AudioStateListenter audioStateListenter) {mListenter = audioStateListenter; }/** * 使用单例实现 AudioManage * @param file * @return */ //DialogManage主要管理Dialog,Dialog主要依赖Context,而且此Context必须是Activity的Context, //如果DialogManage写成单例实现,将是Application级别的,将无法释放,容易造成内存泄露,甚至导致错误 public static AudioManage getInstance(File file) {mInstance = new AudioManage(file); // if (mInstance == null) { // synchronized (AudioManage.class) { // 同步 // if (mInstance == null) { // mInstance = new AudioManage(file); // } // } // } return mInstance; }/** * 准备录音 */ public void prepareAudio() {new Thread(new Runnable() {@Override public void run() {try {fos = new FileOutputStream(file); } catch (FileNotFoundException e) {// TODO Auto-generated catch block e.printStackTrace(); }VoAACEncoder vo = new VoAACEncoder(); int min = AudioRecord.getMinBufferSize(SAMPLERATE,// 得到一个需要的最小录音缓存大小,SAMPLERATE:采样率 AudioFormat.CHANNEL_IN_MONO,// 声道设置,单声道 AudioFormat.ENCODING_PCM_16BIT);// 编码制式 vo.Init(SAMPLERATE, 16000, (short) 1, (short) 1);// 采样率:8000,bitRate:16000,声道数:1,编码:0.raw // 1.ADTS if (min < 2048) {min = 2048; }byte[] temp = new byte[2048]; mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,// 麦克风音源 SAMPLERATE, AudioFormat.CHANNEL_IN_DEFAULT, AudioFormat.ENCODING_PCM_16BIT, min); mAudioRecord.startRecording(); isRecodering = true; startTime = System.currentTimeMillis(); updateMicStatus(); while (isRecodering) {r = mAudioRecord.read(temp, 0, min); if (r > 0) {v = 0; for (int i = 0; i < temp.length; i++) {v += temp[i] * temp[i]; }byte[] ret = vo.Enc(temp); try {fos.write(ret); } catch (IOException e) {// TODO Auto-generated catch block isRecodering = false; e.printStackTrace(); }}}mAudioRecord.stop(); mAudioRecord.release(); mAudioRecord = null; vo.Uninit(); try {fos.close(); } catch (IOException e) {// TODO Auto-generated catch block e.printStackTrace(); }}}).start(); }private void updateMicStatus() {new Thread(new Runnable() {@Override public void run() {// TODO Auto-generated method stub if (mHandler != null) {while (isRecodering) {int value = (int) (Math.abs((int) (v / (float) r) / 100) >> 1); // 得到即时声音的振幅(测试的是0-30之间) System.out.println("=====振幅="+value); mHandler.sendEmptyMessage(value); SystemClock.sleep(SPACE); }}}}).start(); }/** * 获得音量等级——通过mMediaRecorder获得振幅,然后换算成声音Level * maxLevel最大为7; * mMediaRecorder.getMaxAmplitude() / 32768:0——1; * maxLevel * mMediaRecorder.getMaxAmplitude() / 32768 + 1: 1——7; * @param maxLevel * @return */ public int getVoiceLevel(int maxLevel) {if (isRecodering) {try {//mMediaRecorder.getMaxAmplitude()——获得最大振幅:1-32767 // return maxLevel * mMediaRecorder.getMaxAmplitude() / 32768 + 1; int value = (int) (Math.abs((int) (v / (float) r) / 100) >> 1); // 得到即时声音的振幅(测试的是0-30之间) System.out.println("=====振幅="+value); return maxLevel*value/30; } catch (Exception e) {// TODO Auto-generated catch block // e.printStackTrace(); }}return 1; }/** * 释放资源 */ public void release() { // mMediaRecorder.stop(); // mMediaRecorder.release(); // mMediaRecorder = null; isRecodering=false; }/** * 取消(释放资源+删除文件) */ public void cancel() {release(); // if (mCurrentFilePath != null) { // File file = new File(mCurrentFilePath); // file.delete(); //删除录音文件 // mCurrentFilePath = null; // } } }
最好就是dialog的封装,并且对于音量振幅动画
import android.app.Dialog; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.TextView; /***************************************************** * author: wz * email: wangzhong0116@foxmail.com * version: 1.0 * date: 2016/12/6 09:57 * description: 管理录音提示对话框 * 不同提示对话框显示原理:不同状态,显示部分View,隐藏部分View,然后居中显示 *****************************************************/ public class AudioDialogManage {private Dialog mDialog; private ImageView mIcon; //左侧图标 private ImageView mVoice; //声音展示 private TextView mLabel; private Context mContext; public AudioDialogManage(Context context) {this.mContext = context; }/** * 默认的对话框的显示 */ public void showRecorderingDialog() {mDialog = new Dialog(mContext, R.style.Theme_AudioDialog); LayoutInflater inflater = LayoutInflater.from(mContext); View view = inflater.inflate(R.layout.voicenotes_recorder_dialog, null); mDialog.setContentView(view); mIcon = (ImageView) mDialog.findViewById(R.id.recorder_dialog_icon); mVoice = (ImageView) mDialog.findViewById(R.id.recorder_dialog_voice); mLabel = (TextView) mDialog.findViewById(R.id.recorder_dialog_label); mDialog.show(); }//下面在显示各种对话框时,mDialog已经被构造,只需要控制ImageView、TextView的显示即可 /** * 正在录音时,Dialog的显示 */ public void recording() {if (mDialog != null && mDialog.isShowing()) {mIcon.setVisibility(View.VISIBLE); mVoice.setVisibility(View.VISIBLE); mLabel.setVisibility(View.VISIBLE); mIcon.setImageResource(R.mipmap.recorder); mLabel.setText("手指滑动,取消录音"); }}/** * 取消录音提示对话框 */ public void wantToCancel() {if (mDialog != null && mDialog.isShowing()) {mIcon.setVisibility(View.VISIBLE); mVoice.setVisibility(View.GONE); mLabel.setVisibility(View.VISIBLE); mIcon.setImageResource(R.mipmap.cancel); mLabel.setText("松开手指,取消录音"); }}/** * 录音时间过短 */ public void tooShort() {if (mDialog != null && mDialog.isShowing()) {mIcon.setVisibility(View.VISIBLE); mVoice.setVisibility(View.GONE); mLabel.setVisibility(View.VISIBLE); mIcon.setImageResource(R.mipmap.voice_to_short); mLabel.setText("录音时间过短"); }}/** * mDialog.dismiss(); */ public void dimissDialog() {if (mDialog != null && mDialog.isShowing()) {mDialog.dismiss(); mDialog = null; }}/** * 通过Level更新Voice的图片:V1——V7 * @param level */ public void updateVoiceLevel(int level) {if (mDialog != null && mDialog.isShowing()) {int voiceResId=mContext.getResources().getIdentifier("v"+level, "mipmap", mContext.getPackageName()); //getIdentifier()获取应用包下指定资源的ID mVoice.setImageResource(voiceResId); }} }
input_voice .setAudioFinishRecorderListenter(new AudioRecorderButton.AudioFinishRecorderListenter() {@Override public void onFinish(float seconds, String FilePath) { System.out.println("======录音时间======="+seconds); System.out.println("======录音acc地址===="+FilePath); voiceSeconds= (int) seconds;
//此处做的业务处理===发送语音
sendVoice(FilePath,1); }});
android 发送语音功能和ios交互格式aac相关推荐
- 【HTML5】------- JavaScript 实现网页版HTML5发送语音功能
1.浏览器实现HTML5发送语音功能(转载处:https://www.xuanmo.xin/details/1784) 这是一次后台同事让做的一个功能,但是在手机端兼容不好,Safari清一色不支持, ...
- android集成语音功能
android集成语音功能 ONE Goal , ONE Passion ! 第1步: 预备工作 导入SDK 将开发工具包中libs目录下的Msc.jar和armeabi复制到Android工程的li ...
- Android voice语音功能常见问题汇总,注意细节
文章目录 前言 监听有线耳机.蓝牙耳机(音响)插入 打开扬声器 前言 监听耳机操作 audioManager.setMode问题 监听有线耳机.蓝牙耳机(音响)插入 由于有线耳机.蓝牙耳机是两种传输形 ...
- iOS 仿微信发送语音消息按钮 - 手势按钮(一)
最近在做基于XMPP的IM,开发到发送语音消息的功能.在某度上搜了很久也没有找到适合的方法.索性自己琢磨了一个,提供给大家参考.(其中找到的很多文章都是一个复制另一个的,很烦!没格式没头没尾的.给不了 ...
- 发送语音+讯飞翻译 项目案例
数据结构 public LongSparseArray<RecordBean> recordList=new LongSparseArray<>(); 封装所有相关数据的Bea ...
- 短信截取 android,谷歌Android增加语音操作功能 可语音发送短信
[赛迪网讯]8月13日消息,谷歌发布Android操作系统的语音指令功能"Voice Actions for Android"并且在博客中发布了一个解释这些功能的视频.Androi ...
- android仿微信语音聊天功能,Android仿微信发送语音消息的功能及示例代码
微信的发送语音是有一个向上取消的,我们使用ontouchlistener来监听手势,然后做出相应的操作就行了. 直接上代码: //语音操作对象 private mediaplayer mplayer ...
- Android即时通讯与IOS端发送语音的问题。
现在在做一个即时通讯,要发送语音.大家规定好的是aac文件. 现在是我这边录的发给IOS那边可以播放,我自己录的传到服务器再下载下来自己也可以播放,但是IOS那边录的我down下来之后就不行了.放不了 ...
- Android实现语音发送播放功能以及示例代码
本文链接:https://blog.csdn.net/qq_40785165/article/details/109658968 大家好,我是小黑,一个还没秃头的程序员~~~ 这是我第一次写文章,也是 ...
最新文章
- (Alex note) Create a oracle database
- 《数学之美》第20章 不要把鸡蛋都放到一个篮子里--谈谈最大熵模型
- 常考数据结构和算法:设计LRU缓存结构
- 多索引表 (4)multi_index.hpp源代码
- js原生代码编写一个鼠标在页面移动坐标的检测功能,兼容各大浏览器
- python刷题_11.学习Python,刷题才能让你感受到快(差)乐(距)!
- s1机试补考补习 9206
- HDFS Shell API(常用)
- 女生学Java软件开发好就业吗
- vs2017调用目标发生了异常
- appium的滑动操作总结
- 3D Photography using Context-aware Layered Depth Inpainting-论文主要内容翻译
- Hyperledger Fabric 管理链码 peer lifecycle chaincode 指令使用
- 深圳大学计算机单招,2016年深圳大学美术单招校考时间
- vue3+ts+vant移动端H5项目搭建
- PM应该了解的九大项目管理问题
- 几种解决window10睡眠后WLAN打不开(无法通过WiFi图标唤醒或根本没有WLAN图标)问题的方法
- win10下设置超清晰壁纸
- 戴尔win10重新安装win7系统
- 壬华科技通过CMMI 3级认证