现在很多时候需要用到录音,然后如果我们的App是ios和android两端的话,就要考虑录音的文件在两端都能使用,这个时候就需要适配,两端的录音文件都要是mp3文件,这样才能保证两边都能播放。

针对这个,封装了一个简单可用的录音控件。

使用方法:

1.在xml文件中添加

<ant.muxi.com.audiodemo.view.SoundTextView
 android:id="@ id/record_audio"
 android:text="按住开始录音"
 android:gravity="center"
 android:background="@drawable/bg_round_black"
 android:layout_marginLeft="20dp"
 android:layout_marginRight="20dp"
 android:layout_marginBottom="40px"
 android:padding="20px"
 android:layout_width="match_parent"
 android:layout_height="wrap_content">
 </ant.muxi.com.audiodemo.view.SoundTextView>

2.别忘了申请录音权限

AndPermission.with(MainActivity.this)
  .permission(Manifest.permission.RECORD_AUDIO,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE)
  .onGranted(permissions -> {
   showSelect();
  })
  .onDenied(permissions -> {
   Toast.makeText(MainActivity.this,"请同意录音权限",Toast.LENGTH_SHORT).show();
  })
  .start();
private void showSelect() {
 SoundTextView recordAudio = findViewById(R.id.record_audio);
 recordAudio.setOnRecordFinishedListener(new SoundTextView.OnRecordFinishedListener() {
  @Override
  public void newMessage(String path, int duration) {
  int index = path.lastIndexOf("/");
  String fileName = path.substring(index   1);
  Log.e("录音文件", "path=: " path );
  }
 });
 }

使用方法如上非常简单:

主要的类

package ant.muxi.com.audiodemo.view;
 import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.widget.AppCompatTextView;
import java.io.File;
import ant.muxi.com.audiodemo.R;
import ant.muxi.com.audiodemo.audio.ProgressTextUtils;
import ant.muxi.com.audiodemo.audio.RecordManager;
public class SoundTextView extends AppCompatTextView {
 private Context mContext;
 private Dialog recordIndicator;
 private TextView mVoiceTime;
 private File file;
 private String type = "1";//默认开始录音 type=2,录音完毕
 RecordManager recordManager;
 File fileto;
 int level;
 private long downT;
 String sountime;
 public SoundTextView(Context context) {
 super(context);
 init();
 }
 public SoundTextView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 this.mContext = context;
 init();
 }
 public SoundTextView(Context context, AttributeSet attrs) {
 super(context, attrs);
 this.mContext = context;
 init();
 }
 private void init() {
 recordIndicator = new Dialog(getContext(), R.style.jmui_record_voice_dialog);
 recordIndicator.setContentView(R.layout.jmui_dialog_record_voice);
 mVoiceTime = (TextView) recordIndicator.findViewById(R.id.voice_time);
 file = new File(Environment.getExternalStorageDirectory()   "/recoder.amr");
 fileto = new File(Environment.getExternalStorageDirectory()   "/recoder.mp3");
 recordManager = new RecordManager(
  (Activity) mContext,
  String.valueOf(file),
  String.valueOf(fileto));
 recordManager.setOnAudioStatusUpdateListener(new RecordManager.OnAudioStatusUpdateListener() {
  @Override
  public void onUpdate(double db) {
  //得到分贝
  if (null != recordIndicator) {
   level = (int) db;
   handler.sendEmptyMessage(0x111);
  }
  }
 });
 }
 Handler handler = new Handler() {
 @Override
 public void handleMessage(Message msg) {
  super.handleMessage(msg);
  switch (msg.what) {
  case 0x111:
   sountime = ProgressTextUtils.getSecsProgress(System.currentTimeMillis() - downT);
   long time = System.currentTimeMillis() - downT;
   mVoiceTime.setText(ProgressTextUtils.getProgressText(time));
   //判断时间
   judetime(Integer.parseInt(sountime));
   break;
  }
 }
 };
 public void judetime(int time) {
 if (time > 14) {
  //结束录制
  Toast.makeText(mContext, "录音不能超过十五秒", Toast.LENGTH_SHORT).show();
  recordManager.stop_mp3();
  new Thread() {
  @Override
  public void run() {
   super.run();
   recordManager.saveData();
   finishRecord(fileto.getPath(), sountime);
  }
  }.start();
  recordIndicator.dismiss();
  type = "2";
 }
 }
 @Override
 public boolean onTouchEvent(MotionEvent event) {
 int action = event.getAction();
 switch (action) {
  case MotionEvent.ACTION_DOWN:
  if (type.equals("1")) {
   //开始发送时间
   downT = System.currentTimeMillis();
   recordManager.start_mp3();
   recordIndicator.show();
  } else {
   Log.e("-log-", "您已经录制完毕: ");
  }
  return true;
  case MotionEvent.ACTION_UP:
  if (type.equals("1")) {
   try {
   if (Integer.parseInt(sountime) > 2) {
    recordManager.stop_mp3();
    new Thread() {
    @Override
    public void run() {
     super.run();
     recordManager.saveData();
     finishRecord(fileto.getPath(), sountime);
    }
    }.start();
    if (recordIndicator.isShowing()) {
    recordIndicator.dismiss();
    }
    type = "2";
   } else {
    recordManager.stop_mp3();
    if (recordIndicator.isShowing()) {
    recordIndicator.dismiss();
    }
    sountime = null;
    Toast.makeText(mContext, "录音时间少于3秒,请重新录制", Toast.LENGTH_SHORT).show();
   }
   } catch (Exception e) {
   recordManager.stop_mp3();
   if (recordIndicator.isShowing()) {
    recordIndicator.dismiss();
   }
   sountime = null;
   Toast.makeText(mContext, "录音时间少于3秒,请重新录制", Toast.LENGTH_SHORT).show();
   }
  }
  break;
  case MotionEvent.ACTION_CANCEL:
  if (recordIndicator.isShowing()) {
   recordIndicator.dismiss();
  }
  break;
 }
 return super.onTouchEvent(event);
 }
 //录音完毕加载 ListView item
 private void finishRecord(String path, String time) {
 if (onRecordFinishedListener != null) {
  onRecordFinishedListener.newMessage(path, Integer.parseInt(time));
  type = "1";
 }
 //发送语音
 // Toasts.toast(getContext(),"您已经录完了一条语音" myRecAudioFile);
 }
 private OnRecordFinishedListener onRecordFinishedListener;
 public void setOnRecordFinishedListener(OnRecordFinishedListener onRecordFinishedListener) {
 this.onRecordFinishedListener = onRecordFinishedListener;
 }
 public interface OnRecordFinishedListener {
 void newMessage(String path, int duration);
 }
}

主要的录音管理类

public class RecordManager {
 //录制成MP3格式..............................................
 /**构造时候需要的Activity,主要用于获取文件夹的路径*/
 private Activity activity;
 /**文件代号*/
 public static final int RAW = 0X00000001;
 public static final int MP3 = 0X00000002;
 /**文件路径*/
 private String rawPath = null;
 private String mp3Path = null;
 /**采样频率*/
 private static final int SAMPLE_RATE = 11025;
 /**录音需要的一些变量*/
 private short[] mBuffer;
 private AudioRecord mRecorder;
 /**录音状态*/
 private boolean isRecording = false;
 /**是否转换ok*/
 private boolean convertOk = false;
 public RecordManager(Activity activity, String rawPath, String mp3Path) {
 this.activity = activity;
 this.rawPath = rawPath;
 this.mp3Path = mp3Path;
 }
 /**开始录音*/
 public boolean start_mp3() {
 // 如果正在录音,则返回
 if (isRecording) {
  return isRecording;
 }
 // 初始化
 if (mRecorder == null) {
  initRecorder();
 }
 getFilePath();
 mRecorder.startRecording();
 startBufferedWrite(new File(rawPath));
 isRecording = true;
 return isRecording;
 }
 /**停止录音,并且转换文件,这很可能是个耗时操作,建议在后台中做*/
 public boolean stop_mp3() {
 if (!isRecording) {
  return isRecording;
 }
 // 停止
 mRecorder.stop();
 isRecording = false;
//TODO
 // 开始转换(转换代码就这两句)
// FLameUtils lameUtils = new FLameUtils(1, SAMPLE_RATE, 96);
// convertOk = lameUtils.raw2mp3(rawPath, mp3Path);
// return isRecording ^ convertOk;// convertOk==true,return true
 return isRecording;
 }
 public void saveData(){
 FLameUtils lameUtils = new FLameUtils(1, SAMPLE_RATE, 96);
 convertOk = lameUtils.raw2mp3(rawPath, mp3Path);
 }
 /**获取文件的路径*/
 public String getFilePath(int fileAlias) {
 if (fileAlias == RAW) {
  return rawPath;
 } else if (fileAlias == MP3) {
  return mp3Path;
 } else
  return null;
 }
 /**清理文件*/
 public void cleanFile(int cleanFlag) {
 File f = null;
 try {
  switch (cleanFlag) {
  case MP3:
   f = new File(mp3Path);
   if (f.exists())
   f.delete();
   break;
  case RAW:
   f = new File(rawPath);
   if (f.exists())
   f.delete();
   break;
  case RAW | MP3:
   f = new File(rawPath);
   if (f.exists())
   f.delete();
   f = new File(mp3Path);
   if (f.exists())
   f.delete();
   break;
  }
  f = null;
 } catch (Exception e) {
  e.printStackTrace();
 }
 }
 /**关闭,可以先调用cleanFile来清理文件*/
 public void close() {
 if (mRecorder != null)
  mRecorder.release();
 activity = null;
 }
 /**初始化*/
 private void initRecorder() {
 int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE,
  AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
 mBuffer = new short[bufferSize];
 mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE,
  AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,
  bufferSize);
 }
 /**设置路径,第一个为raw文件,第二个为mp3文件*/
 private void getFilePath() {
 try {
  String folder = "audio_recorder_2_mp3";
  String fileName = String.valueOf(System.currentTimeMillis());
  if (rawPath == null) {
  File raw = new File(activity.getDir(folder,
   activity.MODE_PRIVATE), fileName   ".raw");
  raw.createNewFile();
  rawPath = raw.getAbsolutePath();
  raw = null;
  }
  if (mp3Path == null) {
  File mp3 = new File(activity.getDir(folder,
   activity.MODE_PRIVATE), fileName   ".mp3");
  mp3.createNewFile();
  mp3Path = mp3.getAbsolutePath();
  mp3 = null;
  }
  Log.d("rawPath", rawPath);
  Log.d("mp3Path", mp3Path);
 } catch (Exception e) {
  e.printStackTrace();
 }
 }
 /**执行cmd命令,并等待结果*/
 private boolean runCommand(String command) {
 boolean ret = false;
 Process process = null;
 try {
  process = Runtime.getRuntime().exec(command);
  process.waitFor();
  ret = true;
 } catch (Exception e) {
  e.printStackTrace();
 } finally {
  try {
  process.destroy();
  } catch (Exception e) {
  e.printStackTrace();
  }
 }
 return ret;
 }
 /**写入到raw文件*/
 private void startBufferedWrite(final File file) {
 Object mLock = new Object();
 new Thread(new Runnable() {
  @Override
  public void run() {
  DataOutputStream output = null;
  try {
   output = new DataOutputStream(new BufferedOutputStream(
    new FileOutputStream(file)));
   while (isRecording) {//开始录制
   int readSize = mRecorder.read(mBuffer, 0,
    mBuffer.length);//是实际读取的数据长度
   for (int i = 0; i < readSize; i  ) {
    output.writeShort(mBuffer[i]);
   }
   long v = 0;
   // 将 buffer 内容取出,进行平方和运算
   for (int i = 0; i < mBuffer.length; i  ) {
    v  = mBuffer[i] * mBuffer[i];
   }
   // 平方和除以数据总长度,得到音量大小。
   double mean = v / (double) readSize;
   double volume = 10 * Math.log10(mean);
   synchronized (mLock) {
    try {
    if(null != audioStatusUpdateListener) {
     audioStatusUpdateListener.onUpdate(volume);
    }
    mLock.wait(100);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
   }
   }
  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   if (output != null) {
   try {
    output.flush();
   } catch (IOException e) {
    e.printStackTrace();
   } finally {
    try {
    output.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
   }
   }
  }
  }
 }).start();
 }
 public RecordManager.OnAudioStatusUpdateListener audioStatusUpdateListener;
 public void setOnAudioStatusUpdateListener(RecordManager.OnAudioStatusUpdateListener audioStatusUpdateListener) {
 this.audioStatusUpdateListener = audioStatusUpdateListener;
 }
 public interface OnAudioStatusUpdateListener {
 public void onUpdate(double db);
 }
}

完整代码:http://xiazai.jb51.net/201911/yuanma/AudioDemo_jb51.rar

总结

以上所述是小编给大家介绍的Android仿微信录音功能(录音后的raw文件转mp3文件,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对Devmax网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Android仿微信录音功能(录音后的raw文件转mp3文件)的更多相关文章

  1. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  4. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  5. Ionic – Splash Screen适用于iOS,但不适用于Android

    我有一个离子应用程序,其中使用CLI命令离子资源生成的启动画面和图标iOS版本与正在渲染的启动画面完美配合,但在Android版本中,只有在加载应用程序时才会显示白屏.我检查了config.xml文件,所有路径看起来都是正确的,生成的图像出现在相应的文件夹中.(我使用了splash.psd模板来生成它们.我错过了什么?这是config.xml文件供参考,我觉得我在这里做错了–解决方法在config.xml中添加以下键:它对我有用!

  6. ios – 无法启动iPhone模拟器

    /Library/Developer/CoreSimulator/Devices/530A44CB-5978-4926-9E91-E9DBD5BFB105/data/Containers/Bundle/Application/07612A5C-659D-4C04-ACD3-D211D2830E17/ProductName.app/ProductName然后,如果您在Xcode构建设置中选择标准体系结构并再次构建和运行,则会产生以下结果:dyld:lazysymbolbindingFailed:Symbol

  7. Xamarin iOS图像在Grid内部重叠

    heyo,所以在Xamarin我有一个使用并在其中包含一对,所有这些都包含在内.这在Xamarin.Android中看起来完全没问题,但是在Xamarin.iOS中,图像与标签重叠.我不确定它的区别是什么–为什么它在Xamarin.Android中看起来不错但在iOS中它的全部都不稳定?

  8. 在iOS上向后播放HTML5视频

    我试图在iPad上反向播放HTML5视频.HTML5元素包括一个名为playbackRate的属性,它允许以更快或更慢的速率或相反的方式播放视频.根据Apple’sdocumentation,iOS不支持此属性.通过每秒多次设置currentTime属性,可以反复播放,而无需使用playbackRate.这种方法适用于桌面Safari,但似乎在iOS设备上的搜索限制为每秒1次更新–在我的情况下太慢了.有没有办法在iOS设备上向后播放HTML5视频?解决方法iOS6Safari现在支持playbackRat

  9. 使用 Swift 语言编写 Android 应用入门

    Swift标准库可以编译安卓armv7的内核,这使得可以在安卓移动设备上执行Swift语句代码。做梦,虽然Swift编译器可以胜任在安卓设备上编译Swift代码并运行。这需要的不仅仅是用Swift标准库编写一个APP,更多的是你需要一些框架来搭建你的应用用户界面,以上这些Swift标准库不能提供。简单来说,构建在安卓设备上使用的Swiftstdlib需要libiconv和libicu。通过命令行执行以下命令:gitclonegit@github.com:SwiftAndroid/libiconv-libi

  10. Android – 调用GONE然后VISIBLE使视图显示在错误的位置

    我有两个视图,A和B,视图A在视图B上方.当我以编程方式将视图A设置为GONE时,它将消失,并且它正下方的视图将转到视图A的位置.但是,当我再次将相同的视图设置为VISIBLE时,它会在视图B上显示.我不希望这样.我希望视图B回到原来的位置,这是我认为会发生的事情.我怎样才能做到这一点?编辑–代码}这里是XML:解决方法您可以尝试将两个视图放在RelativeLayout中并相对于彼此设置它们的位置.

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部