在Android中使用MediaRecorder實(shí)現(xiàn)錄音功能?針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡單易行的方法。
成都創(chuàng)新互聯(lián)公司公司2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元黃陵做網(wǎng)站,已為上家服務(wù),為黃陵各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:13518219792
MediaRecorder
已經(jīng)集成了錄音、編碼、壓縮等,支持少量的錄音音頻格式,大概有.aac(API = 16) .amr .3gp
優(yōu)點(diǎn):大部分以及集成,直接調(diào)用相關(guān)接口即可,代碼量小
缺點(diǎn):無法實(shí)時(shí)處理音頻;輸出的音頻格式不是很多,例如沒有輸出mp3格式文件
示例:
使用MediaRecorder類錄音,輸出amr格式文件。錄音20s,輸出的音頻文件大概為33K(已寫測(cè)試代碼)
音頻格式比較
WAV格式:錄音質(zhì)量高,但是壓縮率小,文件大
AAC格式:相對(duì)于mp3,AAC格式的音質(zhì)更佳,文件更??;有損壓縮;一般蘋果或者Android SDK4.1.2(API 16)及以上版本支持播放
AMR格式:壓縮比比較大,但相對(duì)其他的壓縮格式質(zhì)量比較差,多用于人聲,通話錄音
至于常用的mp3格式,使用MediaRecorder沒有該視頻格式輸出。一些人的做法是使用AudioRecord錄音,然后編碼成wav格式,再轉(zhuǎn)換成mp3格式
再貼上一些測(cè)試工程。
功能描述:
1、點(diǎn)擊“錄音WAV文件”,開始錄音。錄音完成后,生成文件/sdcard/FinalAudio.wav
2、點(diǎn)擊“錄音AMR文件”,開始錄音。錄音完成后,生成文件/sdcard/FinalAudio.amr
3、點(diǎn)擊“停止錄音”,停止錄音,并顯示錄音輸出文件以及該文件大小。
大致代碼如下:
1、AudioRecord錄音,封裝成WAV格式
package com.example.audiorecordtest; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import android.media.AudioFormat; import android.media.AudioRecord; public class AudioRecordFunc { // 緩沖區(qū)字節(jié)大小 private int bufferSizeInBytes = 0; //AudioName裸音頻數(shù)據(jù)文件 ,麥克風(fēng) private String AudioName = ""; //NewAudioName可播放的音頻文件 private String NewAudioName = ""; private AudioRecord audioRecord; private boolean isRecord = false;// 設(shè)置正在錄制的狀態(tài) private static AudioRecordFunc mInstance; private AudioRecordFunc(){ } public synchronized static AudioRecordFunc getInstance() { if(mInstance == null) mInstance = new AudioRecordFunc(); return mInstance; } public int startRecordAndFile() { //判斷是否有外部存儲(chǔ)設(shè)備sdcard if(AudioFileFunc.isSdcardExit()) { if(isRecord) { return ErrorCode.E_STATE_RECODING; } else { if(audioRecord == null) creatAudioRecord(); audioRecord.startRecording(); // 讓錄制狀態(tài)為true isRecord = true; // 開啟音頻文件寫入線程 new Thread(new AudioRecordThread()).start(); return ErrorCode.SUCCESS; } } else { return ErrorCode.E_NOSDCARD; } } public void stopRecordAndFile() { close(); } public long getRecordFileSize(){ return AudioFileFunc.getFileSize(NewAudioName); } private void close() { if (audioRecord != null) { System.out.println("stopRecord"); isRecord = false;//停止文件寫入 audioRecord.stop(); audioRecord.release();//釋放資源 audioRecord = null; } } private void creatAudioRecord() { // 獲取音頻文件路徑 AudioName = AudioFileFunc.getRawFilePath(); NewAudioName = AudioFileFunc.getWavFilePath(); // 獲得緩沖區(qū)字節(jié)大小 bufferSizeInBytes = AudioRecord.getMinBufferSize(AudioFileFunc.AUDIO_SAMPLE_RATE, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT); // 創(chuàng)建AudioRecord對(duì)象 audioRecord = new AudioRecord(AudioFileFunc.AUDIO_INPUT, AudioFileFunc.AUDIO_SAMPLE_RATE, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes); } class AudioRecordThread implements Runnable { @Override public void run() { writeDateTOFile();//往文件中寫入裸數(shù)據(jù) copyWaveFile(AudioName, NewAudioName);//給裸數(shù)據(jù)加上頭文件 } } /** * 這里將數(shù)據(jù)寫入文件,但是并不能播放,因?yàn)锳udioRecord獲得的音頻是原始的裸音頻, * 如果需要播放就必須加入一些格式或者編碼的頭信息。但是這樣的好處就是你可以對(duì)音頻的 裸數(shù)據(jù)進(jìn)行處理,比如你要做一個(gè)愛說話的TOM * 貓?jiān)谶@里就進(jìn)行音頻的處理,然后重新封裝 所以說這樣得到的音頻比較容易做一些音頻的處理。 */ private void writeDateTOFile() { // new一個(gè)byte數(shù)組用來存一些字節(jié)數(shù)據(jù),大小為緩沖區(qū)大小 byte[] audiodata = new byte[bufferSizeInBytes]; FileOutputStream fos = null; int readsize = 0; try { File file = new File(AudioName); if (file.exists()) { file.delete(); } fos = new FileOutputStream(file);// 建立一個(gè)可存取字節(jié)的文件 } catch (Exception e) { e.printStackTrace(); } while (isRecord == true) { readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes); if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fos!=null) { try { fos.write(audiodata); } catch (IOException e) { e.printStackTrace(); } } } try { if(fos != null) fos.close();// 關(guān)閉寫入流 } catch (IOException e) { e.printStackTrace(); } } // 這里得到可播放的音頻文件 private void copyWaveFile(String inFilename, String outFilename) { FileInputStream in = null; FileOutputStream out = null; long totalAudioLen = 0; long totalDataLen = totalAudioLen + 36; long longSampleRate = AudioFileFunc.AUDIO_SAMPLE_RATE; int channels = 2; long byteRate = 16 * AudioFileFunc.AUDIO_SAMPLE_RATE * channels / 8; byte[] data = new byte[bufferSizeInBytes]; try { in = new FileInputStream(inFilename); out = new FileOutputStream(outFilename); totalAudioLen = in.getChannel().size(); totalDataLen = totalAudioLen + 36; WriteWaveFileHeader(out, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate); while (in.read(data) != -1) { out.write(data); } in.close(); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * 這里提供一個(gè)頭信息。插入這些信息就可以得到可以播放的文件。 * 為我為啥插入這44個(gè)字節(jié),這個(gè)還真沒深入研究,不過你隨便打開一個(gè)wav * 音頻的文件,可以發(fā)現(xiàn)前面的頭文件可以說基本一樣哦。每種格式的文件都有 * 自己特有的頭文件。 */ private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen, long totalDataLen, long longSampleRate, int channels, long byteRate) throws IOException { byte[] header = new byte[44]; header[0] = 'R'; // RIFF/WAVE header header[1] = 'I'; header[2] = 'F'; header[3] = 'F'; header[4] = (byte) (totalDataLen & 0xff); header[5] = (byte) ((totalDataLen >> 8) & 0xff); header[6] = (byte) ((totalDataLen >> 16) & 0xff); header[7] = (byte) ((totalDataLen >> 24) & 0xff); header[8] = 'W'; header[9] = 'A'; header[10] = 'V'; header[11] = 'E'; header[12] = 'f'; // 'fmt ' chunk header[13] = 'm'; header[14] = 't'; header[15] = ' '; header[16] = 16; // 4 bytes: size of 'fmt ' chunk header[17] = 0; header[18] = 0; header[19] = 0; header[20] = 1; // format = 1 header[21] = 0; header[22] = (byte) channels; header[23] = 0; header[24] = (byte) (longSampleRate & 0xff); header[25] = (byte) ((longSampleRate >> 8) & 0xff); header[26] = (byte) ((longSampleRate >> 16) & 0xff); header[27] = (byte) ((longSampleRate >> 24) & 0xff); header[28] = (byte) (byteRate & 0xff); header[29] = (byte) ((byteRate >> 8) & 0xff); header[30] = (byte) ((byteRate >> 16) & 0xff); header[31] = (byte) ((byteRate >> 24) & 0xff); header[32] = (byte) (2 * 16 / 8); // block align header[33] = 0; header[34] = 16; // bits per sample header[35] = 0; header[36] = 'd'; header[37] = 'a'; header[38] = 't'; header[39] = 'a'; header[40] = (byte) (totalAudioLen & 0xff); header[41] = (byte) ((totalAudioLen >> 8) & 0xff); header[42] = (byte) ((totalAudioLen >> 16) & 0xff); header[43] = (byte) ((totalAudioLen >> 24) & 0xff); out.write(header, 0, 44); } }
2、MediaRecorder錄音,輸出amr格式音頻
package com.example.audiorecordtest; import java.io.File; import java.io.IOException; import android.media.MediaRecorder; public class MediaRecordFunc { private boolean isRecord = false; private MediaRecorder mMediaRecorder; private MediaRecordFunc(){ } private static MediaRecordFunc mInstance; public synchronized static MediaRecordFunc getInstance(){ if(mInstance == null) mInstance = new MediaRecordFunc(); return mInstance; } public int startRecordAndFile(){ //判斷是否有外部存儲(chǔ)設(shè)備sdcard if(AudioFileFunc.isSdcardExit()) { if(isRecord) { return ErrorCode.E_STATE_RECODING; } else { if(mMediaRecorder == null) createMediaRecord(); try{ mMediaRecorder.prepare(); mMediaRecorder.start(); // 讓錄制狀態(tài)為true isRecord = true; return ErrorCode.SUCCESS; }catch(IOException ex){ ex.printStackTrace(); return ErrorCode.E_UNKOWN; } } } else { return ErrorCode.E_NOSDCARD; } } public void stopRecordAndFile(){ close(); } public long getRecordFileSize(){ return AudioFileFunc.getFileSize(AudioFileFunc.getAMRFilePath()); } private void createMediaRecord(){ /* ①Initial:實(shí)例化MediaRecorder對(duì)象 */ mMediaRecorder = new MediaRecorder(); /* setAudioSource/setVedioSource*/ mMediaRecorder.setAudioSource(AudioFileFunc.AUDIO_INPUT);//設(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.DEFAULT); /* 設(shè)置音頻文件的編碼:AAC/AMR_NB/AMR_MB/Default */ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); /* 設(shè)置輸出文件的路徑 */ File file = new File(AudioFileFunc.getAMRFilePath()); if (file.exists()) { file.delete(); } mMediaRecorder.setOutputFile(AudioFileFunc.getAMRFilePath()); } private void close(){ if (mMediaRecorder != null) { System.out.println("stopRecord"); isRecord = false; mMediaRecorder.stop(); mMediaRecorder.release(); mMediaRecorder = null; } } }
3、其他文件
AudioFileFunc.java
package com.example.audiorecordtest; import java.io.File; import android.media.MediaRecorder; import android.os.Environment; public class AudioFileFunc { //音頻輸入-麥克風(fēng) public final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC; //采用頻率 //44100是目前的標(biāo)準(zhǔn),但是某些設(shè)備仍然支持22050,16000,11025 public final static int AUDIO_SAMPLE_RATE = 44100; //44.1KHz,普遍使用的頻率 //錄音輸出文件 private final static String AUDIO_RAW_FILENAME = "RawAudio.raw"; private final static String AUDIO_WAV_FILENAME = "FinalAudio.wav"; public final static String AUDIO_AMR_FILENAME = "FinalAudio.amr"; /** * 判斷是否有外部存儲(chǔ)設(shè)備sdcard * @return true | false */ public static boolean isSdcardExit(){ if (Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) return true; else return false; } /** * 獲取麥克風(fēng)輸入的原始音頻流文件路徑 * @return */ public static String getRawFilePath(){ String mAudioRawPath = ""; if(isSdcardExit()){ String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath(); mAudioRawPath = fileBasePath+"/"+AUDIO_RAW_FILENAME; } return mAudioRawPath; } /** * 獲取編碼后的WAV格式音頻文件路徑 * @return */ public static String getWavFilePath(){ String mAudioWavPath = ""; if(isSdcardExit()){ String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath(); mAudioWavPath = fileBasePath+"/"+AUDIO_WAV_FILENAME; } return mAudioWavPath; } /** * 獲取編碼后的AMR格式音頻文件路徑 * @return */ public static String getAMRFilePath(){ String mAudioAMRPath = ""; if(isSdcardExit()){ String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath(); mAudioAMRPath = fileBasePath+"/"+AUDIO_AMR_FILENAME; } return mAudioAMRPath; } /** * 獲取文件大小 * @param path,文件的絕對(duì)路徑 * @return */ public static long getFileSize(String path){ File mFile = new File(path); if(!mFile.exists()) return -1; return mFile.length(); } }
4、其他文件
ErrorCode.java
package com.example.audiorecordtest; import android.content.Context; import android.content.res.Resources.NotFoundException; public class ErrorCode { public final static int SUCCESS = 1000; public final static int E_NOSDCARD = 1001; public final static int E_STATE_RECODING = 1002; public final static int E_UNKOWN = 1003; public static String getErrorInfo(Context vContext, int vType) throws NotFoundException { switch(vType) { case SUCCESS: return "success"; case E_NOSDCARD: return vContext.getResources().getString(R.string.error_no_sdcard); case E_STATE_RECODING: return vContext.getResources().getString(R.string.error_state_record); case E_UNKOWN: default: return vContext.getResources().getString(R.string.error_unknown); } } }
5、string.xml
<?xml version="1.0" encoding="utf-8"?>AudioRecordTest 測(cè)試AudioRecord,實(shí)現(xiàn)錄音功能 Settings 錄音WAV文件 錄音AMR文件 停止錄音 沒有SD卡,無法存儲(chǔ)錄音數(shù)據(jù) 正在錄音中,請(qǐng)先停止錄音 無法識(shí)別的錯(cuò)誤
6、主程序MainActivity
package com.example.audiorecordtest; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.Menu; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { private final static int FLAG_WAV = 0; private final static int FLAG_AMR = 1; private int mState = -1; //-1:沒再錄制,0:錄制wav,1:錄制amr private Button btn_record_wav; private Button btn_record_amr; private Button btn_stop; private TextView txt; private UIHandler uiHandler; private UIThread uiThread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewByIds(); setListeners(); init(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; } private void findViewByIds(){ btn_record_wav = (Button)this.findViewById(R.id.btn_record_wav); btn_record_amr = (Button)this.findViewById(R.id.btn_record_amr); btn_stop = (Button)this.findViewById(R.id.btn_stop); txt = (TextView)this.findViewById(R.id.text); } private void setListeners(){ btn_record_wav.setOnClickListener(btn_record_wav_clickListener); btn_record_amr.setOnClickListener(btn_record_amr_clickListener); btn_stop.setOnClickListener(btn_stop_clickListener); } private void init(){ uiHandler = new UIHandler(); } private Button.OnClickListener btn_record_wav_clickListener = new Button.OnClickListener(){ public void onClick(View v){ record(FLAG_WAV); } }; private Button.OnClickListener btn_record_amr_clickListener = new Button.OnClickListener(){ public void onClick(View v){ record(FLAG_AMR); } }; private Button.OnClickListener btn_stop_clickListener = new Button.OnClickListener(){ public void onClick(View v){ stop(); } }; /** * 開始錄音 * @param mFlag,0:錄制wav格式,1:錄音amr格式 */ private void record(int mFlag){ if(mState != -1){ Message msg = new Message(); Bundle b = new Bundle();// 存放數(shù)據(jù) b.putInt("cmd",CMD_RECORDFAIL); b.putInt("msg", ErrorCode.E_STATE_RECODING); msg.setData(b); uiHandler.sendMessage(msg); // 向Handler發(fā)送消息,更新UI return; } int mResult = -1; switch(mFlag){ case FLAG_WAV: AudioRecordFunc mRecord_1 = AudioRecordFunc.getInstance(); mResult = mRecord_1.startRecordAndFile(); break; case FLAG_AMR: MediaRecordFunc mRecord_2 = MediaRecordFunc.getInstance(); mResult = mRecord_2.startRecordAndFile(); break; } if(mResult == ErrorCode.SUCCESS){ uiThread = new UIThread(); new Thread(uiThread).start(); mState = mFlag; }else{ Message msg = new Message(); Bundle b = new Bundle();// 存放數(shù)據(jù) b.putInt("cmd",CMD_RECORDFAIL); b.putInt("msg", mResult); msg.setData(b); uiHandler.sendMessage(msg); // 向Handler發(fā)送消息,更新UI } } /** * 停止錄音 */ private void stop(){ if(mState != -1){ switch(mState){ case FLAG_WAV: AudioRecordFunc mRecord_1 = AudioRecordFunc.getInstance(); mRecord_1.stopRecordAndFile(); break; case FLAG_AMR: MediaRecordFunc mRecord_2 = MediaRecordFunc.getInstance(); mRecord_2.stopRecordAndFile(); break; } if(uiThread != null){ uiThread.stopThread(); } if(uiHandler != null) uiHandler.removeCallbacks(uiThread); Message msg = new Message(); Bundle b = new Bundle();// 存放數(shù)據(jù) b.putInt("cmd",CMD_STOP); b.putInt("msg", mState); msg.setData(b); uiHandler.sendMessageDelayed(msg,1000); // 向Handler發(fā)送消息,更新UI mState = -1; } } private final static int CMD_RECORDING_TIME = 2000; private final static int CMD_RECORDFAIL = 2001; private final static int CMD_STOP = 2002; class UIHandler extends Handler{ public UIHandler() { } @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub Log.d("MyHandler", "handleMessage......"); super.handleMessage(msg); Bundle b = msg.getData(); int vCmd = b.getInt("cmd"); switch(vCmd) { case CMD_RECORDING_TIME: int vTime = b.getInt("msg"); MainActivity.this.txt.setText("正在錄音中,已錄制:"+vTime+" s"); break; case CMD_RECORDFAIL: int vErrorCode = b.getInt("msg"); String vMsg = ErrorCode.getErrorInfo(MainActivity.this, vErrorCode); MainActivity.this.txt.setText("錄音失?。?+vMsg); break; case CMD_STOP: int vFileType = b.getInt("msg"); switch(vFileType){ case FLAG_WAV: AudioRecordFunc mRecord_1 = AudioRecordFunc.getInstance(); long mSize = mRecord_1.getRecordFileSize(); MainActivity.this.txt.setText("錄音已停止.錄音文件:"+AudioFileFunc.getWavFilePath()+"\n文件大小:"+mSize); break; case FLAG_AMR: MediaRecordFunc mRecord_2 = MediaRecordFunc.getInstance(); mSize = mRecord_2.getRecordFileSize(); MainActivity.this.txt.setText("錄音已停止.錄音文件:"+AudioFileFunc.getAMRFilePath()+"\n文件大?。?+mSize); break; } break; default: break; } } }; class UIThread implements Runnable { int mTimeMill = 0; boolean vRun = true; public void stopThread(){ vRun = false; } public void run() { while(vRun){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } mTimeMill ++; Log.d("thread", "mThread........"+mTimeMill); Message msg = new Message(); Bundle b = new Bundle();// 存放數(shù)據(jù) b.putInt("cmd",CMD_RECORDING_TIME); b.putInt("msg", mTimeMill); msg.setData(b); MainActivity.this.uiHandler.sendMessage(msg); // 向Handler發(fā)送消息,更新UI } } } }
關(guān)于在Android中使用MediaRecorder實(shí)現(xiàn)錄音功能問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。