简单MVP实现Android录音/播放的demo, AudioRecord录音,AudioTrack播放,可以拖动滚动条播放录音
录音/播放工具类RecordSoundUtils
/**
* Created by pc20170521 on 2017-09-11.
* 录音工具类
1. 创建一个数据流。
2. 构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小可以通过getMinBufferSize方法得到。如果buffer容量过小,将导致对象构造的失败。
3. 初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小。
4. 开始录音。
5. 从AudioRecord中读取声音数据到初始化buffer,将buffer中数据导入数据流。
6. 停止录音。
7. 关闭数据流。
*/
public class RecordSoundUtils {
private static RecordSoundUtils record = null;
//音频获取源
private int audioSource = MediaRecorder.AudioSource.MIC;
//设置采样频率,44100是目前的标准
private int sampleRateInHz = 8000;
//设置输入声道为默认 CHANNEL_IN_DEFAULT 双声道立体声 CHANNEL_IN_STEREO 单声道为 CHANNEL_IN_MONO
private int channelInConfig = AudioFormat.CHANNEL_IN_STEREO;
//设置输出声道为默认 CHANNEL_OUT_DEFAULT 双声道立体声 CHANNEL_OUT_STEREO 单声道为 CHANNEL_OUT_MONO
private int channelOutConfig = AudioFormat.CHANNEL_OUT_STEREO;
//音频数据格式:PCM16位
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
//缓冲区字节大小
private int bufferSizeInBytes = 0;
//录音状态
public static boolean isRecord = false;
private AudioRecord audioRecord = null;
private AudioTrack audioTrack = null;
public static RecordSoundUtils getIntance(){
if (record == null){
synchronized (RecordSoundUtils.class){
if (record == null){
record = new RecordSoundUtils();
}
}
}
return record;
}
/****
*
* @param nChannelOutConfig 通道
* @param nSampleRateInHz 采样频率
*/
public void setFormat(int nChannelOutConfig, int nSampleRateInHz){
if (nChannelOutConfig == 1){
channelOutConfig = AudioFormat.CHANNEL_OUT_DEFAULT;
}else {
channelOutConfig = AudioFormat.CHANNEL_OUT_STEREO;
}
sampleRateInHz = nSampleRateInHz;
}
/***
* 创建AudioRecord
*/
private void createAudioRecord(){
if (audioRecord == null){
//获取缓冲区大小
bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelOutConfig, audioFormat);
audioRecord = new AudioRecord(audioSource, sampleRateInHz, channelOutConfig, audioFormat, bufferSizeInBytes);
}
}
/***
* 获取缓冲区大小
* @return
*/
public int getBufferSize(){
return bufferSizeInBytes;
}
/****
* 返回音频数据
* @param audioData 字节数据
* @param bufferSizeInBytes 缓冲区大小
* @return
*/
public int readAudioData(byte [] audioData, int bufferSizeInBytes){
if (audioRecord != null){
return audioRecord.read(audioData, 0, bufferSizeInBytes);
}else {
return 0;
}
}
/***
* 保存音频文件
*/
private void saveAudioData(){
new Thread(new Runnable() {
@Override
public void run() {
try {
OutputStream os = new FileOutputStream(FileUtils.getIntance().getSoundFlie());
BufferedOutputStream bos = new BufferedOutputStream(os);
DataOutputStream dos = new DataOutputStream(bos);
byte []buffer = new byte[bufferSizeInBytes];
while (isRecord){
int bufferreadAudioResult = readAudioData(buffer, bufferSizeInBytes);
for (int i = 0; i < bufferreadAudioResult; i++) {
dos.writeByte(buffer[i]);
}
}
dos.flush();
bos.flush();
os.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
/****开始录音***/
public void startRecord(){
try {
stopRecord();
createAudioRecord();
audioRecord.startRecording();
isRecord = true;
saveAudioData();//保存音频文件
}catch (Exception e){
e.printStackTrace();
}
}
/****停止录音***/
public void stopRecord(){
try {
if (isRecord){
if (audioRecord != null){
audioRecord.stop();
audioRecord.release();
audioRecord = null;
}
}
isRecord = false;
}catch (Exception e){
e.printStackTrace();
}
}
/**
*音频数据写入audioTrack
* @param audioData
* @param offset
* @param length
*/
public void playAudioTrack(byte []audioData, int offset, int length){
try {
if (audioData == null || length == 0){
return ;
}
audioTrack.write(audioData, offset, length);
}catch (Exception e){
e.printStackTrace();
}
}
/***
* 获取播放缓冲区大小
* @return
*/
public int getplayBufferSize(){
int minSize = AudioTrack.getMinBufferSize(sampleRateInHz, channelOutConfig, audioFormat);
return minSize;
}
/****播放录音***/
public void playRecord(File file, int offset){
try {
if (audioTrack != null){
stopPlay();
}
int length = (int) file.length();
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRateInHz,
channelOutConfig,
audioFormat,
length,
AudioTrack.MODE_STREAM);
audioTrack.play();
FileInputStream is = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
byte []by = new byte[length];
int i = 0;
while (dis.available() > 0){
by[i] = dis.readByte();
i++;
}
dis.close();
bis.close();
is.close();
playAudioTrack(by, offset, length);
// stopPlay();
}catch (Exception e){
e.printStackTrace();
}
}
/****停止播放***/
public void stopPlay(){
try {
if (audioTrack != null){
// audioTrack.stop();
audioTrack.release();
audioTrack = null;
}
}catch (Exception e){
e.printStackTrace();
}
}
}
model类IPlayRecordModel 接口
/**
* Created by pc20170521 on 2017-09-11.
*
*/
public interface IPlayRecordModel {
long getPlayRecordTime(File file);
}
model类PlayRecordModel 实现
/**
* Created by pc20170521 on 2017-09-11.
* 返回播放总时间毫秒数
*/
public class PlayRecordModel implements IPlayRecordModel {
@Override
public long getPlayRecordTime(File file) {
long time = 0;
if (file != null && file.exists()){
String startTime = file.getName().split("\\.")[0];
time = file.lastModified() - DateUtils.getDateToLong(startTime);
}
return time;
}
}
view类IPlayRecordView 接口
/**
* Created by pc20170521 on 2017-09-11.
*/
public interface IPlayRecordView {
void setStartTime(long startTime);
void setProgress(int progress);
void setEndTime(long endTime);
}
PlayRecordPresenter 类
/**
* Created by pc20170521 on 2017-09-11.
* 数据与UI的分离
*/
public class PlayRecordPresenter {
private IPlayRecordModel iPlayRecordModel;
private IPlayRecordView iPlayRecordView;
public PlayRecordPresenter(IPlayRecordView view){
iPlayRecordModel = new PlayRecordModel();
iPlayRecordView = view;
}
public void setFileInfo(File file){
long time = iPlayRecordModel.getPlayRecordTime(file);
iPlayRecordView.setStartTime(System.currentTimeMillis());
iPlayRecordView.setProgress(0);
iPlayRecordView.setEndTime(time);
}
}
PlayRecordActivity 类
录音只需要调一下工具类就可以了,这里只写播放的,有播放进度条当前播放时间、总时间
/**
*Created by pc20170521 on 2017-09-11.
* 播放pcm格式
*/
public class PlayRecordActivity extends BaseActivity implements IPlayRecordView {
private SeekBar sb_record;
private TextView tv_startTime;
private TextView tv_endTime;
private PlayRecordPresenter presenter;
private long updateTime = 0;
private long startTime = 0;//开始播放时间
private long endTime = 0;//总时长
private long speed = 0;//当前位置
private long totalSize = 0;//总时间秒数
private int mProgress = 0;
private int mProgressMax = 60;
private long nowProgress = 0;
private File file;
//默认时区
private int timeZone = 8;
//各个时区列表
private String [] timeItemCity = {"Etc/GMT+12", "Pacific/Samoa" , "Pacific/Honolulu", "America/Anchorage", "America/Tijuana", "America/Denver",
"America/Monterrey", "America/Havana" , "America/Asuncion", "America/Bahia", "America/Noronha", "Atlantic/Azores", "Africa/Timbuktu",
"Africa/Luanda", "Africa/Lusaka", "Asia/Bahrain", "Indian/Mauritius", "Asia/Karachi", "Asia/Thimphu", "Asia/Hovd",
"Asia/Shanghai", "Asia/Jayapura", "Australia/Hobart", "Pacific/Ponape", "Pacific/Tarawa"};
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0://播放完成
nowProgress = mProgressMax;
sb_record.setProgress(mProgressMax);
handler.removeCallbacks(myRunnable);
RecordSoundUtils.getIntance().stopPlay();
ToastUtils.show(MyApplication.context, getString(R.string.playrecord_ok));
break;
case 1:
handler.removeCallbacks(myRunnable);
break;
}
}
};
private Runnable myRunnable = new Runnable() {
@Override
public void run() {
if (System.currentTimeMillis() - updateTime > 990){
speed ++;
updateTime = System.currentTimeMillis();
long time = System.currentTimeMillis() - startTime - timeZone*1000*60*60;
//设置播放时间推移
tv_startTime.setText(DateUtils.stampToDate(time + "", DateUtils.HH_MM_SS));
if (time >= endTime){//播放完成,更新UI
handler.sendEmptyMessage(0);
}else {//设置进度条
if (totalSize == 0){
handler.sendEmptyMessage(0);
return;
}
long progress = speed*mProgressMax/totalSize;
nowProgress = progress;
sb_record.setProgress((int) progress);
}
}
handler.postDelayed(this, 10);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.play_record);
initView();
addListener();
}
@Override
public void initView() {
sb_record = (SeekBar) findViewById(R.id.sb_record);
tv_startTime = (TextView) findViewById(R.id.tv_startTime);
tv_endTime = (TextView) findViewById(R.id.tv_endTime);
//判断当前时区,确保国外播放时间也是正确的
for (int i = 0; i < timeItemCity.length; i ++){
if (timeItemCity[i].equals(TimeZone.getDefault().getID())){
timeZone = i - 12;
break;
}
}
speed = 0;
//创建PlayRecordPresenter对象
presenter = new PlayRecordPresenter(this);
String fileName = getIntent().getStringExtra("fileName");
file = FileUtils.getIntance().getFlieByName(fileName);
presenter.setFileInfo(file);
//开始播放
RecordSoundUtils.getIntance().playRecord(file, 0);
//更新UI
handler.postDelayed(myRunnable, 10);
//总播放时间秒数
totalSize = (endTime - (System.currentTimeMillis() - startTime - timeZone*1000*60*60))/1000 + 2;
}
@Override
public void addListener() {
//拖动进度条
sb_record.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
mProgress = progress;
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
presenter.setFileInfo(file);
//计算已播放多少秒
speed = mProgress*totalSize/mProgressMax;
if (speed - 1> 0){
speed = speed - 1;
}else {
speed = 0;
}
startTime = startTime - (speed*1000);
//根据当前拖动位置播放
RecordSoundUtils.getIntance().playRecord(file, (int) (speed*file.length()/totalSize));
if (nowProgress >= mProgressMax){
handler.postDelayed(myRunnable, 10);
}
}
});
}
@Override
public void setStartTime(long startTime) {
this.startTime = startTime;
}
@Override
public void setEndTime(long endTime) {
this.endTime = endTime - timeZone*1000*60*60;
tv_endTime.setText(DateUtils.stampToDate(this.endTime + "", DateUtils.HH_MM_SS));
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacks(myRunnable);
RecordSoundUtils.getIntance().stopPlay();
}
}
时间工具类
public class DateUtils {
public static String YY_MM_DDHH_MM_SS = "yyyy-MM-dd HH:mm:ss";
public static String YY_MM_DD = "yyyy-MM-dd";
public static String YYMMDD = "yyyyMMdd";
public static String YY_MM = "yyyy-MM";
public static String YY_MM_DDHH_MM = "yyyy-MM-dd HH:mm";
public static String MM_DDHH_MM = "MM-dd HH:mm";
public static String HH_MM = "HH:mm";
public static String HH_MM_SS = "HH:mm:ss";
/***获取yy-mm-ss格式*/
@SuppressLint("SimpleDateFormat")
public static String getDateTime(String timeType){
if ("".equals(timeType) || null == timeType)return "";
SimpleDateFormat df = new SimpleDateFormat(timeType);//设置日期格式
return df.format(new Date());// new Date()为获取当前系统时间
/**
* 字符串转日期,格式为:"yyyy-MM-dd HH:mm:ss"
* @param dateStr
* @return
*/
public static long getDateToLong(String dateStr){
if ("".equals(dateStr) || null == dateStr)return 0;
SimpleDateFormat sdf=new SimpleDateFormat(YY_MM_DDHH_MM_SS);
Date result=null;
try {
result = sdf.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return result.getTime();
}
/*
* 将时间戳转换为时间
*/
public static String stampToDate(String s, String type){
String res;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(type);
long lt = new Long(s);
Date date = new Date(lt);
res = simpleDateFormat.format(date);
return res;
}
}