Android提供了兩個API用于錄音的實(shí)現(xiàn):MediaRecorder 和 AudioRecord侮邀,各有優(yōu)劣函筋。
1、MediaRecorder
已經(jīng)集成了錄音再愈、編碼榜苫、壓縮等,支持少量的錄音音頻格式翎冲,大概有.aac(API = 16) .amr .3gp
優(yōu)點(diǎn):大部分已經(jīng)集成垂睬,直接調(diào)用相關(guān)接口即可,代碼量小
缺點(diǎn):無法實(shí)時(shí)處理音頻;輸出的音頻格式不是很多驹饺,例如沒有輸出mp3格式文件
2钳枕、AudioRecord
主要是實(shí)現(xiàn)邊錄邊播(AudioRecord+AudioTrack)以及對音頻的實(shí)時(shí)處理(如會說話的湯姆貓、語音)
優(yōu)點(diǎn):語音的實(shí)時(shí)處理赏壹,可以用代碼實(shí)現(xiàn)各種音頻的封裝
缺點(diǎn):輸出是PCM語音數(shù)據(jù)鱼炒,如果保存成音頻文件,是不能夠被播放器播放的蝌借,所以必須先寫代碼實(shí)現(xiàn)數(shù)據(jù)編碼以及壓縮
先說 MediaRecorder : MediaRecorder因?yàn)榇蟛糠止δ芤呀?jīng)集成昔瞧,所以使用起來相對比較簡單。
下面是個小demo:
① 界面
界面比較簡單菩佑,由于MediaRecorder 并不能實(shí)現(xiàn)暫停自晰、繼續(xù)錄音的功能
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="開始" />
<Button
android:id="@+id/btn_stop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:text="停止" />
<TextView
android:id="@+id/text_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="00:00:00"
android:padding="5dp"
android:layout_marginTop="10dp"/>
</LinearLayout>
② 相關(guān)錄音功能
開始錄音
public void startRecord() {
// 開始錄音
/* ①Initial:實(shí)例化MediaRecorder對象 */
if (mMediaRecorder == null)
mMediaRecorder = new MediaRecorder();
try {
/* ②setAudioSource/setVedioSource */
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 設(shè)置麥克風(fēng)
/*
* ②設(shè)置輸出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式
* ,H263視頻/ARM音頻編碼)稍坯、MPEG-4缀磕、RAW_AMR(只支持音頻且音頻編碼要求為AMR_NB)
*/
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
/* ②設(shè)置音頻文件的編碼:AAC/AMR_NB/AMR_MB/Default 聲音的(波形)的采樣 */
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
fileName = DateFormat.format("yyyyMMdd_HHmmss", Calendar.getInstance(Locale.CHINA)) + ".m4a";
if (!FileUtils.isFolderExist(FileUtils.getFolderName(audioSaveDir))) {
FileUtils.makeFolders(audioSaveDir);
}
filePath = audioSaveDir + fileName;
/* ③準(zhǔn)備 */
mMediaRecorder.setOutputFile(filePath);
mMediaRecorder.prepare();
/* ④開始 */
mMediaRecorder.start();
} catch (IllegalStateException e) {
LogUtil.i("call startAmr(File mRecAudioFile) failed!" + e.getMessage());
} catch (IOException e) {
LogUtil.i("call startAmr(File mRecAudioFile) failed!" + e.getMessage());
}
}
音頻編碼可以根據(jù)自己實(shí)際需要自己設(shè)定,文件名防止重復(fù)劣光,使用了日期_時(shí)分秒的結(jié)構(gòu),audioSaveDir 是文件存儲目錄糟把,可自行設(shè)定绢涡。
停止錄音
public void stopRecord() {
try {
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
filePath = "";
} catch (RuntimeException e) {
LogUtil.e(e.toString());
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
File file = new File(filePath);
if (file.exists())
file.delete();
filePath = "";
}
}
時(shí)長記錄
// 記錄錄音時(shí)長
private void countTime() {
while (isRecording) {
LogUtil.d("正在錄音");
timeCount++;
Message msg = Message.obtain();
msg.what = TIME_COUNT;
msg.obj = timeCount;
myHandler.sendMessage(msg);
try {
timeThread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
LogUtil.d("結(jié)束錄音");
timeCount = 0;
Message msg = Message.obtain();
msg.what = TIME_COUNT;
msg.obj = timeCount;
myHandler.sendMessage(msg);
}
將錄音時(shí)長格式化
// 格式化 錄音時(shí)長為 時(shí):分:秒
public static String FormatMiss(int miss) {
String hh = miss / 3600 > 9 ? miss / 3600 + "" : "0" + miss / 3600;
String mm = (miss % 3600) / 60 > 9 ? (miss % 3600) / 60 + "" : "0" + (miss % 3600) / 60;
String ss = (miss % 3600) % 60 > 9 ? (miss % 3600) % 60 + "" : "0" + (miss % 3600) % 60;
return hh + ":" + mm + ":" + ss;
}
Activity全部代碼
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.zzx.hellokotlin.R;
import com.zzx.hellokotlin.utils.FileUtils;
import com.zzx.hellokotlin.utils.LogUtil;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.Locale;
public class Record2Activity extends AppCompatActivity {
// 錄音界面相關(guān)
Button btnStart;
Button btnStop;
TextView textTime;
// 錄音功能相關(guān)
MediaRecorder mMediaRecorder; // MediaRecorder 實(shí)例
boolean isRecording; // 錄音狀態(tài)
String fileName; // 錄音文件的名稱
String filePath; // 錄音文件存儲路徑
Thread timeThread; // 記錄錄音時(shí)長的線程
int timeCount; // 錄音時(shí)長 計(jì)數(shù)
final int TIME_COUNT = 0x101;
// 錄音文件存放目錄
final String audioSaveDir = Environment.getExternalStorageDirectory().getAbsolutePath()+"/audiodemo/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_record2);
btnStart = (Button) findViewById(R.id.btn_start);
btnStop = (Button) findViewById(R.id.btn_stop);
textTime = (TextView) findViewById(R.id.text_time);
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 開始錄音
btnStart.setEnabled(false);
btnStop.setEnabled(true);
startRecord();
isRecording = true;
// 初始化錄音時(shí)長記錄
timeThread = new Thread(new Runnable() {
@Override
public void run() {
countTime();
}
});
timeThread.start();
}
});
btnStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 停止錄音
btnStart.setEnabled(true);
btnStop.setEnabled(false);
stopRecord();
isRecording = false;
}
});
}
// 記錄錄音時(shí)長
private void countTime() {
while (isRecording) {
LogUtil.d("正在錄音");
timeCount++;
Message msg = Message.obtain();
msg.what = TIME_COUNT;
msg.obj = timeCount;
myHandler.sendMessage(msg);
try {
timeThread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
LogUtil.d("結(jié)束錄音");
timeCount = 0;
Message msg = Message.obtain();
msg.what = TIME_COUNT;
msg.obj = timeCount;
myHandler.sendMessage(msg);
}
/**
* 開始錄音 使用amr格式
* 錄音文件
* @return
*/
public void startRecord() {
// 開始錄音
/* ①Initial:實(shí)例化MediaRecorder對象 */
if (mMediaRecorder == null)
mMediaRecorder = new MediaRecorder();
try {
/* ②setAudioSource/setVedioSource */
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 設(shè)置麥克風(fēng)
/*
* ②設(shè)置輸出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式
* ,H263視頻/ARM音頻編碼)遣疯、MPEG-4雄可、RAW_AMR(只支持音頻且音頻編碼要求為AMR_NB)
*/
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
/* ②設(shè)置音頻文件的編碼:AAC/AMR_NB/AMR_MB/Default 聲音的(波形)的采樣 */
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
fileName = DateFormat.format("yyyyMMdd_HHmmss", Calendar.getInstance(Locale.CHINA)) + ".m4a";
if (!FileUtils.isFolderExist(FileUtils.getFolderName(audioSaveDir))) {
FileUtils.makeFolders(audioSaveDir);
}
filePath = audioSaveDir + fileName;
/* ③準(zhǔn)備 */
mMediaRecorder.setOutputFile(filePath);
mMediaRecorder.prepare();
/* ④開始 */
mMediaRecorder.start();
} catch (IllegalStateException e) {
LogUtil.i("call startAmr(File mRecAudioFile) failed!" + e.getMessage());
} catch (IOException e) {
LogUtil.i("call startAmr(File mRecAudioFile) failed!" + e.getMessage());
}
}
/**
* 停止錄音
*/
public void stopRecord() {
//有一些網(wǎng)友反應(yīng)在5.0以上在調(diào)用stop的時(shí)候會報(bào)錯,翻閱了一下谷歌文檔發(fā)現(xiàn)上面確實(shí)寫的有可能會報(bào)錯的情況缠犀,捕獲異常清理一下就行了数苫,感謝大家反饋!
try {
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
filePath = "";
} catch (RuntimeException e) {
LogUtil.e(e.toString());
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
File file = new File(filePath);
if (file.exists())
file.delete();
filePath = "";
}
}
// 格式化 錄音時(shí)長為 時(shí):分:秒
public static String FormatMiss(int miss) {
String hh = miss / 3600 > 9 ? miss / 3600 + "" : "0" + miss / 3600;
String mm = (miss % 3600) / 60 > 9 ? (miss % 3600) / 60 + "" : "0" + (miss % 3600) / 60;
String ss = (miss % 3600) % 60 > 9 ? (miss % 3600) % 60 + "" : "0" + (miss % 3600) % 60;
return hh + ":" + mm + ":" + ss;
}
Handler myHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TIME_COUNT:
int count = (int) msg.obj;
LogUtil.d("count == " + count);
textTime.setText(FormatMiss(count));
break;
}
}
};
}
總結(jié):MediaRecorder 實(shí)現(xiàn)錄音還是比較簡單的辨液,只是不能暫停虐急。