最近研究了下Android MediaRecorder的錄音功能,發現暫停之後,繼續錄音這個功能,網上參考的資料比較少,現在將自己的學習成果分享大家:
基本原理如下:MediaRecorder通過MIC錄音,系統沒有自帶的pause功能,每次暫停錄音,都會結束本次的錄音。現在本人的設計思路是:MediaRecorder錄音暫停時,保存這段所錄下的音頻A,繼續錄音後,再次暫停,保留錄音音頻B;以此類推直到最終的錄音結束時,依次讀取之前保存的A、B……的錄音文件,並將其合並在一起。涉及的技術:文件的保存讀取、音頻的合並等
音頻的合並:設置MediaRecorder的音頻輸出格式mMediaRecorder01.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
mMediaRecorder01 .setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);輸出的是amr格式。amr的音頻文件的文件頭,相對來說是固定的6個字節的固定字符,A.amr文件和B.amr文件的合並,只需將B以字節流讀取,去掉前6個字節,和A的字節流合並後保存,就實現了音頻合並,不涉及復雜的音頻編碼問題。(MediaRecorder的音頻輸出格式比較多,有jpgg、MP3等之類的格式,合成的原理大同小異,只需要注意他們的音頻文件頭的格式就可以了。)
資源代碼:
免費下載地址在 http://linux.linuxidc.com/
用戶名與密碼都是www.linuxidc.com
具體下載目錄在 /2012年資料/7月/4日/Android MediaRecorder實現暫停斷點錄音功能
有圖有真相:
- public class EX07 extends Activity {
- private ImageButton myButton1;
- private ImageButton myButton2;
- private ImageButton myButton3;
- private ImageButton myButton4;
- private Button myButton;
- private ListView myListView1;
- private String strTempFile = "YYT_";
- private File myRecAudioFile;
- /**錄音保存路徑**/
- private File myRecAudioDir;
- private File myPlayFile;
- private MediaRecorder mMediaRecorder01;
- private int mMinute;
- private boolean xx=true;
- /**存放音頻文件列表**/
- private ArrayList<String> recordFiles;
- private ArrayAdapter<String> adapter;
- private TextView myTextView1;
- /**文件存在**/
- private boolean sdcardExit;
- /**是否停止錄音**/
- private boolean isStopRecord;
- /**按鈕背景圖片的標志位**/
- private boolean sigle = false;
- private String length1 = null;
-
- private final String SUFFIX=".amr";
-
-
- /**暫停按鈕**/
- Button buttonpause;
-
-
- /**記錄需要合成的幾段amr語音文件**/
- private ArrayList<String> list;
-
-
- int second=0;
-
- int minute=0;
-
- /**計時器**/
- Timer timer;
-
-
- /**是否暫停標志位**/
- private boolean isPause;
-
- /**在暫停狀態中**/
- private boolean inThePause;
-
-
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
-
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- //暫停標志位 為false
- isPause=false;
- //暫停狀態標志位
- inThePause=false;
-
- //初始化list
- list=new ArrayList<String>();
-
- //四個按鈕
- myButton1 = (ImageButton) findViewById(R.id.ImageButton01);
- myButton2 = (ImageButton) findViewById(R.id.ImageButton02);
- myButton3 = (ImageButton) findViewById(R.id.ImageButton03);
- myButton4 = (ImageButton) findViewById(R.id.ImageButton04);
- myButton = (Button) findViewById(R.id.myButton);
- buttonpause=(Button)findViewById(R.id.mypuase);
- myListView1 = (ListView) findViewById(R.id.ListView01);
- myTextView1 = (TextView) findViewById(R.id.TextView01);
- myButton2.setEnabled(false);
- myButton3.setEnabled(false);
- myButton4.setEnabled(false);
-
- myPlayFile=null;
-
- // 判斷sd Card是否插入
- sdcardExit = Environment.getExternalStorageState().equals(
- android.os.Environment.MEDIA_MOUNTED);
- // 取得sd card路徑作為錄音文件的位置
- if (sdcardExit){
- String pathStr = Environment.getExternalStorageDirectory().getPath()+"/YYT";
- myRecAudioDir= new File(pathStr);
- if(!myRecAudioDir.exists()){
- myRecAudioDir.mkdirs();
- Log.v("錄音", "創建錄音文件!" + myRecAudioDir.exists());
- }
- // Environment.getExternalStorageDirectory().getPath() + "/" + PREFIX + "/";
- }
- // 取得sd card 目錄裡的.arm文件
- getRecordFiles();
-
- adapter = new ArrayAdapter<String>(this,
- android.R.layout.simple_list_item_1, recordFiles);
- // 將ArrayAdater添加ListView對象中
- myListView1.setAdapter(adapter);
- // 錄音
-
- myButton1.setOnClickListener(new ImageButton.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- second=0;
- minute=0;
-
- list.clear();
- // Calendar c=Calendar.getInstance();
- // int mMinute1=c.get(Calendar.MINUTE);
-
- sigle = true;
- // TODO Auto-generated method stub
-
- start();
-
- if (sigle = false) {
- myButton1.setBackgroundResource(R.drawable.record_hover1);
- } else {
- myButton1.setBackgroundResource(R.drawable.record_dis1);
- myButton2.setBackgroundResource(R.drawable.stop_hover2);
- myButton3.setBackgroundResource(R.drawable.play_hover1);
- myButton4.setBackgroundResource(R.drawable.delete_hover);
- }
-
-
- }
-
- });
- // 停止
- myButton2.setOnClickListener(new ImageButton.OnClickListener() {
-
- @Override
- public void onClick(View v) {
-
-
- xx=false;
- sigle = true;
- timer.cancel();
- // TODO Auto-generated method stub
-
-
- //這裡寫暫停處理的 文件!加上list裡面 語音合成起來
- if(isPause){
-
- //在暫停狀態按下結束鍵,處理list就可以了
- if(inThePause){
- getInputCollection(list, false);
- }
- //在正在錄音時,處理list裡面的和正在錄音的語音
- else{
- list.add(myRecAudioFile.getPath());
- recodeStop();
- getInputCollection(list, true);
- }
-
- //還原標志位
- isPause=false;
- inThePause=false;
- buttonpause.setText("暫停錄音");
-
-
-
-
- // adapter.add(myRecAudioFile.getName());
-
- }
-
-
-
- //若錄音沒有經過任何暫停
- else{
-
-
- if (myRecAudioFile != null) {
- // 停止錄音
- mMediaRecorder01.stop();
- mMediaRecorder01.release();
- mMediaRecorder01 = null;
- // 將錄音頻文件給Adapter
- adapter.add(myRecAudioFile.getName());
- DecimalFormat df = new DecimalFormat("#.000");
- if (myRecAudioFile.length() <= 1024*1024) {
- //length1 = (myRecAudioFile.length() / 1024.0)+"";
-
- length1=df.format(myRecAudioFile.length() / 1024.0)+"K";
- } else {
- //length1 = (myRecAudioFile.length() / 1024.0 / 1024)+"";
- //DecimalFormat df = new DecimalFormat("#.000");
- length1=df.format(myRecAudioFile.length() / 1024.0 / 1024)+"M";
- }
- System.out.println(length1);
-
- myTextView1.setText("停 止" + myRecAudioFile.getName()
- + "文件大小為:" + length1);
- myButton2.setEnabled(false);
-
- }
-
- }
-
- if (sigle = false) {
- myButton2.setBackgroundResource(R.drawable.stop_hover2);
- } else {
- myButton1.setBackgroundResource(R.drawable.record_hover1);
- myButton2.setBackgroundResource(R.drawable.stop1);
- myButton3.setBackgroundResource(R.drawable.play_hover1);
- myButton4.setBackgroundResource(R.drawable.delete_hover);
- }
-
- //停止錄音了
- isStopRecord = true;
- }
-
- });
-
- // 播放
- myButton3.setOnClickListener(new ImageButton.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- sigle = true;
- // TODO Auto-generated method stub
- if (myPlayFile != null && myPlayFile.exists()) {
- // 打開播放程序
- openFile(myPlayFile);
- } else {
- Toast.makeText(EX07.this, "你選的是一個空文件", Toast.LENGTH_LONG)
- .show();
- Log.d("沒有選擇文件","沒有選擇文件");
- }
- if (sigle = false) {
- myButton3.setBackgroundResource(R.drawable.play_hover1);
- } else {
- myButton1.setBackgroundResource(R.drawable.record_hover1);
- myButton2.setBackgroundResource(R.drawable.stop_hover2);
- myButton3.setBackgroundResource(R.drawable.play1);
- myButton4.setBackgroundResource(R.drawable.delete_hover);
- }
- }
-
- });
-
- // 刪除
- myButton4.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- sigle = true;
- // TODO Auto-generated method stub
-
- if (myPlayFile != null) {
- // 先將Adapter刪除文件名
- adapter.remove(myPlayFile.getName());
- // 刪除文件
- if (myPlayFile.exists())
- myPlayFile.delete();
- myTextView1.setText("完成刪除!");
-
- }
- if (sigle = false) {
- myButton4.setBackgroundResource(R.drawable.delete_hover);
- } else {
- myButton1.setBackgroundResource(R.drawable.record_hover1);
- myButton2.setBackgroundResource(R.drawable.stop_hover2);
- myButton3.setBackgroundResource(R.drawable.play_hover1);
- myButton4.setBackgroundResource(R.drawable.delete_dis);
- }
- }
- });
-
- /**
- * 暫停按鈕,記錄之前保存的語音文件
- */
- buttonpause.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
-
- isPause=true;
-
- //已經暫停過了,再次點擊按鈕 開始錄音,錄音狀態在錄音中
- if(inThePause){
- buttonpause.setText("暫停錄音");
- start();
- inThePause=false;
-
-
- }
- //正在錄音,點擊暫停,現在錄音狀態為暫停
- else{
-
- //當前正在錄音的文件名,全程
- list.add(myRecAudioFile.getPath());
- inThePause=true;
- recodeStop();
- //start();
- buttonpause.setText("繼續錄音");
-
- //計時停止
- timer.cancel();
- }
- }
- });
-
-
- myListView1
- .setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> arg, View arg1,
- int arg2, long arg3) {
- // TODO Auto-generated method stub
- // 當有單點擊文件名時將刪除按鈕及播放按鈕Enable
- myButton3.setEnabled(true);
- myButton4.setEnabled(true);
- myPlayFile = new File(myRecAudioDir.getAbsolutePath()
- + File.separator
- + ((TextView) arg1).getText().toString());
-
- DecimalFormat df = new DecimalFormat("#.000");
- if (myPlayFile.length() <= 1024*1024) {
- length1=df.format(myPlayFile.length() / 1024.0)+"K";
- } else {
- length1=df.format(myPlayFile.length() / 1024.0/1024)+"M";
- }
- myTextView1.setText("你選的是"
- + ((TextView) arg1).getText().toString()
- + "文件大小為:" + length1);
- Toast.makeText(EX07.this,"你選的是" + (((TextView) arg1).getText())+ "文件大小為:" + length1,
- Toast.LENGTH_LONG).show();
-
- }
-
- });
-
- myButton.setOnClickListener(new Button.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- showSize show = new showSize();
- String text = show.showsize();
- Toast.makeText(EX07.this, text, Toast.LENGTH_LONG).show();
- }
- });
- }
-
-
- protected void recodeStop() {
- if (mMediaRecorder01 != null && !isStopRecord) {
- // 停止錄音
- mMediaRecorder01.stop();
- mMediaRecorder01.release();
- mMediaRecorder01 = null;
- }
-
- timer.cancel();
- }
-
-
- /**
- * activity的生命周期,stop時關閉錄音資源
- */
- @Override
- protected void onStop() {
- // TODO Auto-generated method stub
- if (mMediaRecorder01 != null && !isStopRecord) {
- // 停止錄音
- mMediaRecorder01.stop();
- mMediaRecorder01.release();
- mMediaRecorder01 = null;
- }
- super.onStop();
- }
-
-
- /**
- * 獲取目錄下的所有音頻文件
- */
- private void getRecordFiles() {
- // TODO Auto-generated method stub
- recordFiles = new ArrayList<String>();
- if (sdcardExit) {
- File files[] = myRecAudioDir.listFiles();
- if (files != null) {
- for (int i = 0; i < files.length; i++) {
- if (files[i].getName().indexOf(".") >= 0) { // 只取.amr 文件
- String fileS = files[i].getName().substring(
- files[i].getName().indexOf("."));
- if (fileS.toLowerCase().equals(".mp3")
- || fileS.toLowerCase().equals(".amr")
- || fileS.toLowerCase().equals(".mp4"))
- recordFiles.add(files[i].getName());
-
- }
- }
- }
- }
-
- }
-
- // 打開錄音播放程序
- private void openFile(File f) {
- Intent intent = new Intent();
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setAction(android.content.Intent.ACTION_VIEW);
- String type = getMIMEType(f);
- intent.setDataAndType(Uri.fromFile(f), type);
- startActivity(intent);
- // Uri uri=Uri.fromFile(f);
- // MediaPlayer mediaPlayer=MediaPlayer.create(this, uri);
- // try {
- // mediaPlayer.prepare();
- // } catch (IllegalStateException e) {
- // // TODO Auto-generated catch block
- // e.printStackTrace();
- // } catch (IOException e) {
- // // TODO Auto-generated catch block
- // e.printStackTrace();
- // }
- // mediaPlayer.start();
- }
-
- private String getMIMEType(File f) {
-
- String end = f.getName().substring(f.getName().lastIndexOf(".") + 1,
- f.getName().length()).toLowerCase();
- String type = "";
- if (end.equals("mp3") || end.equals("aac") || end.equals("amr")
- || end.equals("mpeg") || end.equals("mp4")) {
- type = "audio";
- } else if (end.equals("jpg") || end.equals("gif") || end.equals("png")
- || end.equals("jpeg")) {
- type = "image";
- } else {
- type = "*";
- }
- type += "/";
- return type;
- }
-
- private void start(){
-
-
- TimerTask timerTask=new TimerTask() {
-
- @Override
- public void run() {
- // TODO Auto-generated method stub
- second++;
- if(second>=60){
- second=0;
- minute++;
- }
- handler.sendEmptyMessage(1);
- }
- };
- timer=new Timer();
- timer.schedule(timerTask, 1000,1000);
-
- try {
- if (!sdcardExit) {
- Toast.makeText(EX07.this, "請插入SD card",
- Toast.LENGTH_LONG).show();
- return;
- }
- String mMinute1=getTime();
- Toast.makeText(EX07.this, "當前時間是:"+mMinute1,Toast.LENGTH_LONG).show();
- // 創建音頻文件
- // myRecAudioFile = File.createTempFile(mMinute1, ".amr",
- // myRecAudioDir);
-
- myRecAudioFile=new File(myRecAudioDir,mMinute1+SUFFIX);
- mMediaRecorder01 = new MediaRecorder();
- // 設置錄音為麥克風
- mMediaRecorder01
- .setAudioSource(MediaRecorder.AudioSource.MIC);
- mMediaRecorder01
- .setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
- mMediaRecorder01
- .setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
-
- //錄音文件保存這裡
- mMediaRecorder01.setOutputFile(myRecAudioFile
- .getAbsolutePath());
- mMediaRecorder01.prepare();
- mMediaRecorder01.start();
-
- // mMediaRecorder01.getMaxAmplitude();
- // mMediaRecorder01.getAudioSourceMax();
- mMediaRecorder01.setOnInfoListener(new OnInfoListener() {
-
- @Override
- public void onInfo(MediaRecorder mr, int what, int extra) {
- // TODO Auto-generated method stub
- int a=mr.getMaxAmplitude();
- Toast.makeText(EX07.this, a, 500).show();
- }
- });
-
- myTextView1.setText("錄音中......");
- myButton2.setEnabled(true);
- myButton3.setEnabled(false);
- myButton4.setEnabled(false);
- isStopRecord = false;
- } catch (IOException e) {
- e.printStackTrace();
-
- }
-
- }
- private String getTime(){
- SimpleDateFormat formatter = new SimpleDateFormat ("yyyy年MM月dd日HH:mm:ss");
- Date curDate=new Date(System.currentTimeMillis());//獲取當前時間
- String time = formatter.format(curDate);
- System.out.println("當前時間");
- return time;
- }
-
- Handler handler=new Handler(){
-
- @Override
- public void handleMessage(Message msg) {
- // TODO Auto-generated method stub
- super.handleMessage(msg);
-
- myTextView1.setText("錄音時間:"+minute+":"+second);
- }
-
- };
-
- /**
- * @param isAddLastRecord 是否需要添加list之外的最新錄音,一起合並
- * @return 將合並的流用字符保存
- */
- public void getInputCollection(List list,boolean isAddLastRecord){
-
-
- String mMinute1=getTime();
- Toast.makeText(EX07.this, "當前時間是:"+mMinute1,Toast.LENGTH_LONG).show();
-
- // 創建音頻文件,合並的文件放這裡
- File file1=new File(myRecAudioDir,mMinute1+SUFFIX);
- FileOutputStream fileOutputStream = null;
-
- if(!file1.exists()){
- try {
- file1.createNewFile();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- try {
- fileOutputStream=new FileOutputStream(file1);
-
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- //list裡面為暫停錄音 所產生的 幾段錄音文件的名字,中間幾段文件的減去前面的6個字節頭文件
-
-
-
-
- for(int i=0;i<list.size();i++){
- File file=new File((String) list.get(i));
- Log.d("list的長度", list.size()+"");
- try {
- FileInputStream fileInputStream=new FileInputStream(file);
- byte []myByte=new byte[fileInputStream.available()];
- //文件長度
- int length = myByte.length;
-
- //頭文件
- if(i==0){
- while(fileInputStream.read(myByte)!=-1){
- fileOutputStream.write(myByte, 0,length);
- }
- }
-
- //之後的文件,去掉頭文件就可以了
- else{
- while(fileInputStream.read(myByte)!=-1){
-
- fileOutputStream.write(myByte, 6, length-6);
- }
- }
-
- fileOutputStream.flush();
- fileInputStream.close();
- System.out.println("合成文件長度:"+file1.length());
-
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
-
-
- }
- //結束後關閉流
- try {
- fileOutputStream.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- //加上當前正在錄音的這一段
- // if(isAddLastRecord){
- //
- //
- // //剛剛錄音的
- // try {
- // FileInputStream fileInputStream=new FileInputStream(myRecAudioFile);
- // byte []myByte=new byte[fileInputStream.available()];
- // System.out.println(fileInputStream.available()+"");
- // while(fileInputStream.read(myByte)!=-1){
- // //outputStream.
- // fileOutputStream.write(myByte, 6, (fileInputStream.available()-6));
- // }
- //
- // fileOutputStream.flush();
- // fileInputStream.close();
- // fileOutputStream.close();
- // System.out.println("合成文件長度:"+file1.length());
- // } catch (Exception e) {
- // // TODO Auto-generated catch block
- // e.printStackTrace();
- // }
- //
- // }
-
-
- //合成一個文件後,刪除之前暫停錄音所保存的零碎合成文件
- deleteListRecord(isAddLastRecord);
- //
- adapter.add(file1.getName());
-
- }
-
- private void deleteListRecord(boolean isAddLastRecord){
- for(int i=0;i<list.size();i++){
- File file=new File((String) list.get(i));
- if(file.exists()){
- file.delete();
- }
- }
- //正在暫停後,繼續錄音的這一段音頻文件
- if(isAddLastRecord){
- myRecAudioFile.delete();
- }
- }
- }