Android利用AudioRecord類實(shí)現(xiàn)音頻錄制程序
AudioRecord類相對(duì)于MediaRecorder來說,更加接近底層,為我們封裝的方法也更少。然而實(shí)現(xiàn)一個(gè)AudioRecord的音頻錄制程序也很簡(jiǎn)單。本實(shí)例代碼如下:
package demo.camera;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.hardware.Camera.AutoFocusCallback;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
/**
* 該實(shí)例中,我們使用AudioRecord類來完成我們的音頻錄制程序
* AudioRecord類,我們可以使用三種不同的read方法來完成錄制工作,
* 每種方法都有其實(shí)用的場(chǎng)合
* 一、實(shí)例化一個(gè)AudioRecord類我們需要傳入幾種參數(shù)
* 1、AudioSource:這里可以是MediaRecorder.AudioSource.MIC
* 2、SampleRateInHz:錄制頻率,可以為8000hz或者11025hz等,不同的硬件設(shè)備這個(gè)值不同
* 3、ChannelConfig:錄制通道,可以為AudioFormat.CHANNEL_CONFIGURATION_MONO和AudioFormat.CHANNEL_CONFIGURATION_STEREO
* 4、AudioFormat:錄制編碼格式,可以為AudioFormat.ENCODING_16BIT和8BIT,其中16BIT的仿真性比8BIT好,但是需要消耗更多的電量和存儲(chǔ)空間
* 5、BufferSize:錄制緩沖大?。嚎梢酝ㄟ^getMinBufferSize來獲取
* 這樣我們就可以實(shí)例化一個(gè)AudioRecord對(duì)象了
* 二、創(chuàng)建一個(gè)文件,用于保存錄制的內(nèi)容
* 同上篇
* 三、打開一個(gè)輸出流,指向創(chuàng)建的文件
* DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))
* 四、現(xiàn)在就可以開始錄制了,我們需要?jiǎng)?chuàng)建一個(gè)字節(jié)數(shù)組來存儲(chǔ)從AudioRecorder中返回的音頻數(shù)據(jù),但是
* 注意,我們定義的數(shù)組要小于定義AudioRecord時(shí)指定的那個(gè)BufferSize
* short[]buffer = new short[BufferSize/4];
* startRecording();
* 然后一個(gè)循環(huán),調(diào)用AudioRecord的read方法實(shí)現(xiàn)讀取
* 另外使用MediaPlayer是無法播放使用AudioRecord錄制的音頻的,為了實(shí)現(xiàn)播放,我們需要
* 使用AudioTrack類來實(shí)現(xiàn)
* AudioTrack類允許我們播放原始的音頻數(shù)據(jù)
*
*
* 一、實(shí)例化一個(gè)AudioTrack同樣要傳入幾個(gè)參數(shù)
* 1、StreamType:在AudioManager中有幾個(gè)常量,其中一個(gè)是STREAM_MUSIC;
* 2、SampleRateInHz:最好和AudioRecord使用的是同一個(gè)值
* 3、ChannelConfig:同上
* 4、AudioFormat:同上
* 5、BufferSize:通過AudioTrack的靜態(tài)方法getMinBufferSize來獲取
* 6、Mode:可以是AudioTrack.MODE_STREAM和MODE_STATIC,關(guān)于這兩種不同之處,可以查閱文檔
* 二、打開一個(gè)輸入流,指向剛剛錄制內(nèi)容保存的文件,然后開始播放,邊讀取邊播放
*
* 實(shí)現(xiàn)時(shí),音頻的錄制和播放分別使用兩個(gè)AsyncTask來完成
*/
public class MyAudioRecord2 extends Activity{
private TextView stateView;
private Button btnStart,btnStop,btnPlay,btnFinish;
private RecordTask recorder;
private PlayTask player;
private File audioFile;
private boolean isRecording=true, isPlaying=false; //標(biāo)記
private int frequence = 8000; //錄制頻率,單位hz.這里的值注意了,寫的不好,可能實(shí)例化AudioRecord對(duì)象的時(shí)候,會(huì)出錯(cuò)。我開始寫成11025就不行。這取決于硬件設(shè)備
private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.my_audio_record);
stateView = (TextView)this.findViewById(R.id.view_state);
stateView.setText("準(zhǔn)備開始");
btnStart = (Button)this.findViewById(R.id.btn_start);
btnStop = (Button)this.findViewById(R.id.btn_stop);
btnPlay = (Button)this.findViewById(R.id.btn_play);
btnFinish = (Button)this.findViewById(R.id.btn_finish);
btnFinish.setText("停止播放");
btnStop.setEnabled(false);
btnPlay.setEnabled(false);
btnFinish.setEnabled(false);
//在這里我們創(chuàng)建一個(gè)文件,用于保存錄制內(nèi)容
File fpath = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/data/files/");
fpath.mkdirs();//創(chuàng)建文件夾
try {
//創(chuàng)建臨時(shí)文件,注意這里的格式為.pcm
audioFile = File.createTempFile("recording", ".pcm", fpath);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void onClick(View v){
int id = v.getId();
switch(id){
case R.id.btn_start:
//開始錄制
//這里啟動(dòng)錄制任務(wù)
recorder = new RecordTask();
recorder.execute();
break;
case R.id.btn_stop:
//停止錄制
this.isRecording = false;
//更新狀態(tài)
//在錄制完成時(shí)設(shè)置,在RecordTask的onPostExecute中完成
break;
case R.id.btn_play:
player = new PlayTask();
player.execute();
break;
case R.id.btn_finish:
//完成播放
this.isPlaying = false;
break;
}
}
class RecordTask extends AsyncTask<Void, Integer, Void>{
@Override
protected Void doInBackground(Void... arg0) {
isRecording = true;
try {
//開通輸出流到指定的文件
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(audioFile)));
//根據(jù)定義好的幾個(gè)配置,來獲取合適的緩沖大小
int bufferSize = AudioRecord.getMinBufferSize(frequence, channelConfig, audioEncoding);
//實(shí)例化AudioRecord
AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.MIC, frequence, channelConfig, audioEncoding, bufferSize);
//定義緩沖
short[] buffer = new short[bufferSize];
//開始錄制
record.startRecording();
int r = 0; //存儲(chǔ)錄制進(jìn)度
//定義循環(huán),根據(jù)isRecording的值來判斷是否繼續(xù)錄制
while(isRecording){
//從bufferSize中讀取字節(jié),返回讀取的short個(gè)數(shù)
//這里老是出現(xiàn)buffer overflow,不知道是什么原因,試了好幾個(gè)值,都沒用,TODO:待解決
int bufferReadResult = record.read(buffer, 0, buffer.length);
//循環(huán)將buffer中的音頻數(shù)據(jù)寫入到OutputStream中
for(int i=0; i<bufferReadResult; i++){
dos.writeShort(buffer[i]);
}
publishProgress(new Integer(r)); //向UI線程報(bào)告當(dāng)前進(jìn)度
r++; //自增進(jìn)度值
}
//錄制結(jié)束
record.stop();
Log.v("The DOS available:", "::"+audioFile.length());
dos.close();
} catch (Exception e) {
// TODO: handle exception
}
return null;
}
//當(dāng)在上面方法中調(diào)用publishProgress時(shí),該方法觸發(fā),該方法在UI線程中被執(zhí)行
protected void onProgressUpdate(Integer...progress){
stateView.setText(progress[0].toString());
}
protected void onPostExecute(Void result){
btnStop.setEnabled(false);
btnStart.setEnabled(true);
btnPlay.setEnabled(true);
btnFinish.setEnabled(false);
}
protected void onPreExecute(){
//stateView.setText("正在錄制");
btnStart.setEnabled(false);
btnPlay.setEnabled(false);
btnFinish.setEnabled(false);
btnStop.setEnabled(true);
}
}
class PlayTask extends AsyncTask<Void, Integer, Void>{
@Override
protected Void doInBackground(Void... arg0) {
isPlaying = true;
int bufferSize = AudioTrack.getMinBufferSize(frequence, channelConfig, audioEncoding);
short[] buffer = new short[bufferSize/4];
try {
//定義輸入流,將音頻寫入到AudioTrack類中,實(shí)現(xiàn)播放
DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(audioFile)));
//實(shí)例AudioTrack
AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, frequence, channelConfig, audioEncoding, bufferSize, AudioTrack.MODE_STREAM);
//開始播放
track.play();
//由于AudioTrack播放的是流,所以,我們需要一邊播放一邊讀取
while(isPlaying && dis.available()>0){
int i = 0;
while(dis.available()>0 && i<buffer.length){
buffer[i] = dis.readShort();
i++;
}
//然后將數(shù)據(jù)寫入到AudioTrack中
track.write(buffer, 0, buffer.length);
}
//播放結(jié)束
track.stop();
dis.close();
} catch (Exception e) {
// TODO: handle exception
}
return null;
}
protected void onPostExecute(Void result){
btnPlay.setEnabled(true);
btnFinish.setEnabled(false);
btnStart.setEnabled(true);
btnStop.setEnabled(false);
}
protected void onPreExecute(){
//stateView.setText("正在播放");
btnStart.setEnabled(false);
btnStop.setEnabled(false);
btnPlay.setEnabled(false);
btnFinish.setEnabled(true);
}
}
}
可惜,本實(shí)例測(cè)試時(shí)有個(gè)問題,在錄制的時(shí)候,會(huì)出現(xiàn)buffer over。緩存泄露,待解決。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android單項(xiàng)綁定MVVM項(xiàng)目模板的方法
這篇文章主要給大家介紹了關(guān)于Android單項(xiàng)綁定MVVM項(xiàng)目模板的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
基于Android應(yīng)用中如何反饋Crash報(bào)告的詳解
本篇文章是對(duì)在Android應(yīng)用中如何反饋Crash報(bào)告的詳細(xì)分析介紹。需要的朋友參考下2013-05-05
Android ListView數(shù)據(jù)的分批顯示功能
本文通過實(shí)例代碼給大家分享了Android ListView數(shù)據(jù)的分批顯示功能,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友參考下吧2017-04-04
android輸入框內(nèi)容改變的監(jiān)聽事件實(shí)例
下面小編就為大家分享一篇android輸入框內(nèi)容改變的監(jiān)聽事件實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-02-02
Android Wear計(jì)時(shí)器開發(fā)
這篇文章主要介紹了Android Wear計(jì)時(shí)器開發(fā),需要的朋友可以參考下2014-11-11
Android應(yīng)用實(shí)踐之?dāng)?shù)獨(dú)游戲開發(fā)
這篇文章主要為大家詳細(xì)介紹了Android應(yīng)用實(shí)踐之?dāng)?shù)獨(dú)游戲開發(fā),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android.mk引入第三方j(luò)ar包和so庫文件的方法
這篇文章主要介紹了Android.mk引入第三方j(luò)ar包和so庫文件的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
Android開發(fā)使用HttpURLConnection進(jìn)行網(wǎng)絡(luò)編程詳解【附源碼下載】
這篇文章主要介紹了Android開發(fā)使用HttpURLConnection進(jìn)行網(wǎng)絡(luò)編程的方法,結(jié)合實(shí)例形式分析了Android基于HttpURLConnection實(shí)現(xiàn)顯示圖片與文本功能,涉及Android布局、文本解析、數(shù)據(jù)傳輸、權(quán)限控制等相關(guān)操作技巧,需要的朋友可以參考下2018-01-01

