編輯:Android開發實例
AudioRecord類相對於MediaRecorder來說,更加接近底層,為我們封裝的方法也更少。然而實現一個AudioRecord的音頻錄制程序也很
簡單。本實例代碼如下:
可惜,本實例測試時有個問題,在錄制的時候,會出現buffer over。緩存洩露,待解決。
- 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;
- /**
- * 該實例中,我們使用AudioRecord類來完成我們的音頻錄制程序
- * AudioRecord類,我們可以使用三種不同的read方法來完成錄制工作,
- * 每種方法都有其實用的場合
- * 一、實例化一個AudioRecord類我們需要傳入幾種參數
- * 1、AudioSource:這裡可以是MediaRecorder.AudioSource.MIC
- * 2、SampleRateInHz:錄制頻率,可以為8000hz或者11025hz等,不同的硬件設備這個值不同
- * 3、ChannelConfig:錄制通道,可以為AudioFormat.CHANNEL_CONFIGURATION_MONO和AudioFormat.CHANNEL_CONFIGURATION_STEREO
- * 4、AudioFormat:錄制編碼格式,可以為AudioFormat.ENCODING_16BIT和8BIT,其中16BIT的仿真性比8BIT好,但是需要消耗更多的電量和存儲空間
- * 5、BufferSize:錄制緩沖大小:可以通過getMinBufferSize來獲取
- * 這樣我們就可以實例化一個AudioRecord對象了
- * 二、創建一個文件,用於保存錄制的內容
- * 同上篇
- * 三、打開一個輸出流,指向創建的文件
- * DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))
- * 四、現在就可以開始錄制了,我們需要創建一個字節數組來存儲從AudioRecorder中返回的音頻數據,但是
- * 注意,我們定義的數組要小於定義AudioRecord時指定的那個BufferSize
- * short[]buffer = new short[BufferSize/4];
- * startRecording();
- * 然後一個循環,調用AudioRecord的read方法實現讀取
- * 另外使用MediaPlayer是無法播放使用AudioRecord錄制的音頻的,為了實現播放,我們需要
- * 使用AudioTrack類來實現
- * AudioTrack類允許我們播放原始的音頻數據
- *
- *
- * 一、實例化一個AudioTrack同樣要傳入幾個參數
- * 1、StreamType:在AudioManager中有幾個常量,其中一個是STREAM_MUSIC;
- * 2、SampleRateInHz:最好和AudioRecord使用的是同一個值
- * 3、ChannelConfig:同上
- * 4、AudioFormat:同上
- * 5、BufferSize:通過AudioTrack的靜態方法getMinBufferSize來獲取
- * 6、Mode:可以是AudioTrack.MODE_STREAM和MODE_STATIC,關於這兩種不同之處,可以查閱文檔
- * 二、打開一個輸入流,指向剛剛錄制內容保存的文件,然後開始播放,邊讀取邊播放
- *
- * 實現時,音頻的錄制和播放分別使用兩個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; //標記
- private int frequence = 8000; //錄制頻率,單位hz.這裡的值注意了,寫的不好,可能實例化AudioRecord對象的時候,會出錯。我開始寫成11025就不行。這取決於硬件設備
- 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("准備開始");
- 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);
- //在這裡我們創建一個文件,用於保存錄制內容
- File fpath = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/data/files/");
- fpath.mkdirs();//創建文件夾
- try {
- //創建臨時文件,注意這裡的格式為.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:
- //開始錄制
- //這裡啟動錄制任務
- recorder = new RecordTask();
- recorder.execute();
- break;
- case R.id.btn_stop:
- //停止錄制
- this.isRecording = false;
- //更新狀態
- //在錄制完成時設置,在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)));
- //根據定義好的幾個配置,來獲取合適的緩沖大小
- int bufferSize = AudioRecord.getMinBufferSize(frequence, channelConfig, audioEncoding);
- //實例化AudioRecord
- AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.MIC, frequence, channelConfig, audioEncoding, bufferSize);
- //定義緩沖
- short[] buffer = new short[bufferSize];
- //開始錄制
- record.startRecording();
- int r = 0; //存儲錄制進度
- //定義循環,根據isRecording的值來判斷是否繼續錄制
- while(isRecording){
- //從bufferSize中讀取字節,返回讀取的short個數
- //這裡老是出現buffer overflow,不知道是什麼原因,試了好幾個值,都沒用,TODO:待解決
- int bufferReadResult = record.read(buffer, 0, buffer.length);
- //循環將buffer中的音頻數據寫入到OutputStream中
- for(int i=0; i<bufferReadResult; i++){
- dos.writeShort(buffer[i]);
- }
- publishProgress(new Integer(r)); //向UI線程報告當前進度
- r++; //自增進度值
- }
- //錄制結束
- record.stop();
- Log.v("The DOS available:", "::"+audioFile.length());
- dos.close();
- } catch (Exception e) {
- // TODO: handle exception
- }
- return null;
- }
- //當在上面方法中調用publishProgress時,該方法觸發,該方法在UI線程中被執行
- 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類中,實現播放
- DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(audioFile)));
- //實例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++;
- }
- //然後將數據寫入到AudioTrack中
- track.write(buffer, 0, buffer.length);
- }
- //播放結束
- 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);
- }
- }
- }
自定義控件(類似按鈕等)的使用,自定義一個SurfaceView。 如某一塊的動態圖(自定義相應),或者類似UC浏覽器下面的工具欄。 如下圖示例: 自
注冊很多app或者網絡賬戶的時候,經常需要手機獲取驗證碼,來完成注冊,那時年少,只是覺得手機獲取驗
話說為了防止偷拍,業內有不成文規定,手機公司在做camera時,點擊拍照和錄像鍵的時候,必須要有提示音。因此,google也就非常人性化的將播放拍照聲音的函數,放
在java中可有兩種方式實現多線程,一種是繼承Thread類,一種是實現Runnable接口;Thread類是在java.lang包中定義的。一個類只要繼承了Th