最近在做一个类似微信,QQ语音录制,上传数据库,并进行播放,根据录制的时间长短,显示出来的语音条长短也会发生变化,以及在录制时动画等,以及适应iOS,对录制的音频,进行格式转换.
下面就让我们看一下具体的实现吧
先看需要添加的权限,
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 用于获取wifi的获取权限,wifi信息会用来进行网络定位 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!-- 用于访问网络,网络定位需要上网 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 在SD卡中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<!-- 用于写入缓存数据到扩展存储卡 -->
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
有些权限需要动态申请的
/**
* 动态添加权限
*/
public void getPression() {
int checkSelfPermission = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (checkSelfPermission == PackageManager.PERMISSION_DENIED) {
//没有权限,申请权限
ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.CAMERA,
Manifest.permission.WRITE_SETTINGS,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CALL_PHONE}, 100);
} else if (checkSelfPermission == PackageManager.PERMISSION_GRANTED) {
//已经有了权限 ,不需要申请
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 100:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(getContext(), "已经授权成功了", Toast.LENGTH_SHORT).show();
}
break;
}
}
下面就是正式的来写代码
先在页面上定义个按钮,用来录音
这有一个工具类
public class RecordUtil {
public static final String AUDIO_DIR = Environment
.getExternalStorageDirectory().getAbsolutePath() + "/Horusch/audio";
private String mName;
private String mAudioPath; // 要播放的声音的路�?
private boolean recording = false;// 是否正在录音
private boolean playing = false;// 是否正在播放
public MediaRecorder mRecorder;
public MediaPlayer mPlay;
public boolean isRecording() {
return recording;
}
public void setRecording(boolean recording) {
this.recording = recording;
}
public boolean isPlaying() {
return playing;
}
public void setPlaying(boolean playing) {
this.playing = playing;
}
public String getmName() {
return mName;
}
public void setmName(String mName) {
this.mName = mName;
}
public String getmAudioPath() {
return mAudioPath;
}
public void setmAudioPath(String mAudioPath) {
this.mAudioPath = mAudioPath;
}
// 初始�?录音�?
private void initRecorder() {
}
// �?��录音
public void startRecord() {
if (mRecorder != null)
return;
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
mName = "audio_" + 1;
mAudioPath = AUDIO_DIR + "/" + mName + ".mp3";
// 判断是否存在文件�?
File folder = new File(AUDIO_DIR);
if (!folder.exists()) {
folder.mkdirs();
}
// 判断文件是否存在
File file = new File(mAudioPath);
if (file.exists()) {
int count = 2;
while (true) {
mName = "audio_" + count;
mAudioPath = AUDIO_DIR + "/" + mName + ".mp3";
file = new File(mAudioPath);
if (file.exists()) {
count++;
} else {
break;
}
}
}
mRecorder.setOutputFile(file.getAbsolutePath());
recording = true;
try {
mRecorder.prepare();
mRecorder.start();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 停止录音
public void stopRecord() {
try {
if (mRecorder != null && recording) {
//设置异常时不崩溃
mRecorder.setOnErrorListener(null);
mRecorder.setOnInfoListener(null);
mRecorder.setPreviewDisplay(null);
mRecorder.stop();
mRecorder.release();
mRecorder = null;
recording = false;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 获取音量值,只是针对录音音量
public int getVolume() {
int volume = 0;
// 录音
if (mRecorder != null && recording) {
volume = mRecorder.getMaxAmplitude() / 650;
Log.d("sdfgasd", volume + "");
if (volume != 0)
// volume = (int) (10 * Math.log(volume) / Math.log(10)) / 7;
volume = (int) (10 * Math.log10(volume)) / 3;
Log.d("db", volume + "");
}
return volume;
}
// �?��播放
public void startPlay(String path, boolean isLooping) {
if (!path.equals("")) {
mPlay = new MediaPlayer();
try {
mPlay.setDataSource(path);
mPlay.prepare();
mPlay.start();
mPlay.setLooping(isLooping);
playing = true;
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// 停止播放
public void stopPlay() {
if (mPlay != null && mPlay.isPlaying()) {
mPlay.stop();
mPlay.release();
mPlay = null;
playing = false;
}
}
}
下面是按钮的触摸事件,我们需要计算录音的时间,所以需要重写这个按钮的触摸事件,,这里还牵扯到当你手移动出去这个按钮,录制录音就会被中断,而不是都是在up的时候停止,所以我们重新复写触摸事件的,应该多重写一个状态MotionEvent.ACTION_CANCEL,下面就这个按钮的触摸事件
/**
* 录音的触摸事件
*/
private void initEvent() {
btnEventRecording.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
currentTime = System.currentTimeMillis();
tvTack.setText("请开始说话");
mRecorduUtil.startRecord();
if (mRecorduUtil.isRecording()) {
//将step2中的linearlayout设为可视
ll_record.setVisibility(View.VISIBLE);
/**
*启动线程<注意!这里不能用handler.post启动Runnable,否则无法removeCallBack>
*/
Thread t = new Thread(mPollTask);
t.start();
}
break;
case MotionEvent.ACTION_UP:
myRecording(currentTime);
break;
case MotionEvent.ACTION_CANCEL:
myRecording(currentTime);
break;
}
return true;
}
});
}
当你在Dwon时,防止阻塞主线程,需要开启一个线程更改语音图片,在录制的时候会有录制的动画,根据声音的变化,而改变,下面是这个线程
/**
* 开启一个线程更改语音图片
*/
private Runnable mPollTask = new Runnable() {
public void run() {
int mVolume = mRecorduUtil.getVolume();
Log.d("volume", mVolume + "");
updateVolume(mVolume);
mHandler.postDelayed(mPollTask, 100);
}
};
这个是语音的图片
// 更新音量图
private void updateVolume(int volume) {
switch (volume) {
case 1:
iv_volume.setImageResource(R.drawable.volume_icon1);
break;
case 2:
iv_volume.setImageResource(R.drawable.volume_icon2);
break;
case 3:
iv_volume.setImageResource(R.drawable.volume_icon3);
break;
case 4:
iv_volume.setImageResource(R.drawable.volume_icon4);
break;
case 5:
iv_volume.setImageResource(R.drawable.volume_icon5);
break;
case 6:
iv_volume.setImageResource(R.drawable.volume_icon6);
break;
case 7:
iv_volume.setImageResource(R.drawable.volume_icon7);
break;
default:
break;
}
}
下面就是开始录音的代码
/**
* 开始录音
*
* @param DownTime
*/
private void myRecording(long DownTime) {
long thisTime = System.currentTimeMillis();
long recordingTime = thisTime - DownTime;
time = (int) (recordingTime / 1000);
ll_record.setVisibility(View.GONE);
if (recordingTime < 1000) {
tvTack.setText("说话时间太短");
ShowToast.show(EventReportActivity.this, "录音时间太短");
mRecorduUtil.stopRecord();
return;
} else {
if(time<=1){
ViewGroup.LayoutParams layoutParams = startRecding.getLayoutParams();
layoutParams.width = 160;
startRecding.setLayoutParams(layoutParams);
}else if (time>1){
int s = time - 1;
ViewGroup.LayoutParams pp = startRecding.getLayoutParams();
if (160 + s * 9 < 500) {
pp.width = 160 + s * 9;
startRecding.setLayoutParams(pp);
} else if (160 + s * 9 >= 500) {
pp.width = 500;
startRecding.setLayoutParams(pp);
}
}
tvStartRecding.setText(time + "''");
mRecorduUtil.stopRecord();
btnEventRecording.setVisibility(View.GONE);
rlStartRecding.setVisibility(View.VISIBLE);
}
mHandler.removeCallbacks(mPollTask);
File folder = new File(RecordUtil.AUDIO_DIR);
File[] files = folder.listFiles();
if (files.length != 0) {
path1 = files[files.length - 1].getAbsolutePath();
File flacFile = new File(path1);
IConvertCallback callback = new IConvertCallback() {
@Override
public void onSuccess(File convertedFile) {
// So fast? Love it!
LogUtils.d("voice-222---", "So fast? Love it!");
LogUtils.d("voice--222--", convertedFile.getAbsolutePath());
path = convertedFile.getAbsolutePath();
}
@Override
public void onFailure(Exception error) {
// Oops! Something went wrong
LogUtils.d("voice-11---", "Oops! Something went wrong");
LogUtils.d("voice-sss---", error.toString());
}
};
AndroidAudioConverter.with(this)
// Your current audio file
.setFile(flacFile)
// Your desired audio format
.setFormat(AudioFormat.MP3)
// An callback to know when conversion is finished
.setCallback(callback)
// Start conversion
.convert();
}
}
录制基本就绪,我们看一下 效果
录音前:
录音中:
录音后:
大致的效果就是这样
下面就是播放了
//TODO:播放录音
ivVoice.setVisibility(View.GONE);
ivVoiceAnimal.setVisibility(View.VISIBLE);
mRecorduUtil.startPlay(path, false);
ivVoiceAnimal.setImageResource(R.drawable.animation_yunyin);
animationDrawable = (AnimationDrawable) ivVoiceAnimal.getDrawable();
animationDrawable.start();
if (time != 0) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
ivVoiceAnimal.setVisibility(View.GONE);
ivVoice.setVisibility(View.VISIBLE);
}
});
}
}, time * 1000);//up时间
} else if (canceltTime != 0) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
ivVoiceAnimal.setVisibility(View.GONE);
ivVoice.setVisibility(View.VISIBLE);
}
});
}
}, canceltTime * 1000);//touch:MotionEvent.ACTION_CANCEL时间
}
转换格式:
添加权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
在Application里面添加
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
AndroidAudioConverter.load(this, new ILoadCallback() {
@Override
public void onSuccess() {
// Great!
}
@Override
public void onFailure(Exception error) {
// FFmpeg is not supported by device
}
});
}
在用到的地方调用
File folder = new File(RecordUtil.AUDIO_DIR);
File[] files = folder.listFiles();
if (files.length != 0) {
path1 = files[files.length - 1].getAbsolutePath();
File flacFile = new File(path1);
IConvertCallback callback = new IConvertCallback() {
@Override
public void onSuccess(File convertedFile) {
// So fast? Love it!
LogUtils.d("voice-222---", "So fast? Love it!");
LogUtils.d("voice--222--", convertedFile.getAbsolutePath());
path = convertedFile.getAbsolutePath();
}
@Override
public void onFailure(Exception error) {
// Oops! Something went wrong
LogUtils.d("voice-11---", "Oops! Something went wrong");
LogUtils.d("voice-sss---", error.toString());
}
};
AndroidAudioConverter.with(this)
// Your current audio file
.setFile(flacFile)
// Your desired audio format
.setFormat(AudioFormat.MP3)
// An callback to know when conversion is finished
.setCallback(callback)
// Start conversion
.convert();
}
基本的录音和播放,以及转换格式就可以了,赶快去试试吧
下面是转换格式的GitHub地址:https://github.com/adrielcafe/AndroidAudioConverter,
图片什么的就不上传了