现在很多时候需要用到录音,然后如果我们的App是ios和android两端的话,就要考虑录音的文件在两端都能使用,这个时候就需要适配,两端的录音文件都要是mp3文件,这样才能保证两边都能播放。
针对这个,封装了一个简单可用的录音控件。
使用方法:1.在xml文件中添加
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);
}
}
完整代码:https://download.csdn.net/download/shihuiyun/11980411