簡介錄音和播放音訊實現
阿新 • • 發佈:2019-01-22
1.MediaRecorder及MediaPlayer:
MediaRecorder類父類是object,位於media包下,是用來錄製音訊和視訊。
下面是錄音模型:
看起來很是複雜的樣子,當然瞭解下流程還是有必要的。至少你知道如果想重新初始化的話,可以直接使用 reset()方法,不管在哪個階段都可以重置,有圖有真相是吧!
其他的話,也沒啥說的,流程都是規範化了的,改動的空間不大,看api好了。
MediaPlayer也位於media包下,直接繼承與object類,可以用來放音訊/視訊的檔案和流。
下面是工作流程:
看起來比錄音的流程更復雜了,更深的東西我們暫時就不深究了。相對於錄音來說,主要多了暫停,回放的狀態,方法中更是添加了迴圈,進度等東西,更多的就不說了,有興趣的童鞋可以慢慢研究,可以對下面的例項進行擴充套件。
首先,我對功能進行了封裝。
/** * 錄音的類,封裝了錄音/播放的開始停止功能 */ public class MyRecord { /** 錄音 */ private MediaRecorder mRecorder; /** 播放 */ private MediaPlayer mPlayer; private String path = ""; public static MyRecord myRecord = null; // 單例 private MyRecord() { mkMyDir(); }; public static synchronized MyRecord getInstance() { if (myRecord == null) { myRecord = new MyRecord(); } return myRecord; } // 在sdcard上建立資料夾 public void mkMyDir() { File dir = new File(Environment.getExternalStorageDirectory() .getAbsolutePath() + "/TestRecord"); path = dir.getAbsolutePath(); dir.mkdir(); } // 獲取資料夾路徑 public String getPath() { return path; } // 開始錄音 public void startRecord(String filePath) { mRecorder = new MediaRecorder(); mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mRecorder.setOutputFile(filePath); mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 設定錄音檔案大小 ,大概是1s 就是1k的大小 // mRecorder.setMaxFileSize(10 * 1024); // 設定錄音的最大時長,貌似這個跟上面的資料差距有點大 ,最大得到的資料是17k多的資料 // mRecorder.setMaxDuration(10 * 1000); try { mRecorder.prepare(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } mRecorder.start(); } // 停止錄音 public void stopRecord() { mRecorder.stop(); mRecorder.release(); mRecorder = null; } // 播放錄音 public void startPlay(String fileName) { mPlayer = new MediaPlayer(); try { mPlayer.setDataSource(fileName); mPlayer.prepare(); mPlayer.start(); } catch (IOException e) { e.printStackTrace(); } } // 停止播放 public void stopPlay() { mPlayer.stop(); mPlayer.release(); mPlayer = null; } }
使用單例,只有一個例項,避免了冗餘。檔案的路徑,其實應該是提供介面的,想想覺得固定了好點,提供給外面的路徑就好了。
然後是使用了:
public class TestVoice extends Activity implements OnClickListener { private Button startR; private Button stopR; private Button startP; private Button stopP; private MyRecord myRecord; private String path = "/test.3gp"; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.acy_testvoice); initView(); } public void initView() { myRecord = MyRecord.getInstance(); path = myRecord.getPath() + path; startR = (Button) findViewById(R.id.startRecord); stopR = (Button) findViewById(R.id.stopRecord); startP = (Button) findViewById(R.id.startPlay); stopP = (Button) findViewById(R.id.stopPlay); startR.setOnClickListener(this); stopR.setOnClickListener(this); startP.setOnClickListener(this); stopP.setOnClickListener(this); } @Override public void onClick(View v) { // TODO Auto-generated method stub if (v == startR) { myRecord.startRecord(path); } else if (v == stopR) { myRecord.stopRecord(); } else if (v == startP) { myRecord.startPlay(path); } else if (v == stopP) { myRecord.stopPlay(); } } }
2.AudioTrack及AudioRecord:
這是通過流讀取操作來錄製/播放音訊檔案的,相對於上面的方法來對解碼器更加依賴來講,這是直接讀取的是pcm檔案。不過好像錄音的效果不咋好,通過對流的處理是可以實現降噪的,在網上沒找到比較好的辦法,在這就不寫了,因為我也不懂那個。
public class AltAudioRecorder extends Activity implements OnClickListener {
private RecordAudio recordTask;
private PlayAudio playTask;
private Button startRecordingButton, stopRecordingButton,
startPlaybackButton, stopPlaybackButton;
private TextView statusText;
File recordingFile;
boolean isRecording = false;
boolean isPlaying = false;
// 要確定在人耳的接收頻率以內,
int frequency = 11025;
int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.acy_record);
statusText = (TextView) this.findViewById(R.id.StatusTextView);
startRecordingButton = (Button) this
.findViewById(R.id.StartRecordingButton);
stopRecordingButton = (Button) this
.findViewById(R.id.StopRecordingButton);
startPlaybackButton = (Button) this
.findViewById(R.id.StartPlaybackButton);
stopPlaybackButton = (Button) this
.findViewById(R.id.StopPlaybackButton);
startRecordingButton.setOnClickListener(this);
stopRecordingButton.setOnClickListener(this);
startPlaybackButton.setOnClickListener(this);
stopPlaybackButton.setOnClickListener(this);
File path = new File(Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/Android/data/AudioRecorder/files/");
path.mkdirs();
try {
recordingFile = File.createTempFile("recording", ".pcm", path);
} catch (Exception e) {
throw new RuntimeException("Couldn't create file on SD card", e);
}
}
public void onClick(View v) {
if (v == startRecordingButton) {
record();
} else if (v == stopRecordingButton) {
stopRecording();
} else if (v == startPlaybackButton) {
play();
} else if (v == stopPlaybackButton) {
stopPlaying();
}
}
//開始錄音
public void play() {
playTask = new PlayAudio();
playTask.execute();
}
//停止錄音
public void stopPlaying() {
isPlaying = false;
}
//開始播放
public void record() {
recordTask = new RecordAudio();
recordTask.execute();
}
//停止播放
public void stopRecording() {
isRecording = false;
}
private class PlayAudio extends AsyncTask<Void, Integer, Void> {
@Override
protected Void doInBackground(Void... params) {
isPlaying = true;
// 返回例項建立後的最好緩衝區
int bufferSize = AudioTrack.getMinBufferSize(frequency,
channelConfiguration, audioEncoding);
short[] audiodata = new short[bufferSize / 4];
try {
DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream(
recordingFile)));
AudioTrack audioTrack = new AudioTrack(
// 流型別
AudioManager.STREAM_MUSIC,
// 音訊資料的取樣率
frequency,
// 聲道 為雙聲道立體聲
channelConfiguration,
// 設定音訊資料塊為16位
audioEncoding,
// 在錄製過程中,音訊資料寫入緩衝區的總數(位元組)
bufferSize,
// 設定模式型別為流
AudioTrack.MODE_STREAM);
audioTrack.play();
while (isPlaying && dis.available() > 0) {
int i = 0;
while (dis.available() > 0 && i < audiodata.length) {
audiodata[i] = dis.readShort();
i++;
}
audioTrack.write(audiodata, 0, audiodata.length);
}
dis.close();
} catch (Throwable t) {
Log.e("AudioTrack", "Playback Failed");
}
return null;
}
}
private class RecordAudio extends AsyncTask<Void, Integer, Void> {
@Override
protected Void doInBackground(Void... params) {
isRecording = true;
try {
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream(
recordingFile)));
int bufferSize = AudioRecord.getMinBufferSize(frequency,
channelConfiguration, audioEncoding);
AudioRecord audioRecord = new AudioRecord(
MediaRecorder.AudioSource.MIC, frequency,
channelConfiguration, audioEncoding, bufferSize);
short[] buffer = new short[bufferSize];
audioRecord.startRecording();
int r = 0;
while (isRecording) {
int bufferReadResult = audioRecord.read(buffer, 0,
bufferSize);
for (int i = 0; i < bufferReadResult; i++) {
dos.writeShort(buffer[i]);
}
publishProgress(new Integer(r));
r++;
}
audioRecord.stop();
dos.close();
} catch (Throwable t) {
Log.e("AudioRecord", "Recording Failed");
}
return null;
}
protected void onProgressUpdate(Integer... progress) {
statusText.setText(progress[0].toString());
}
protected void onPostExecute(Void result) {
}
}
}
注意:
1.android.media.MediaPlayer.finalize() timed out after 10 seconds
在onPause()中呼叫MediaPlayer.release()
,在onStop()中釋放物件:
mPlayer.stop();
mPlayer.release();
mPlayer = null;