Android多媒体学习十:利用AudioRecord类实现自己的音频录制程序

来源:互联网 发布:网络与继续教育学院 编辑:程序博客网 时间:2024/05/18 06:23

AudioRecord类相对于MediaRecorder来说,更加接近底层,为我们封装的方法也更少。然而实现一个AudioRecord的音频录制程序也很

简单。本实例代码如下:

 

可惜,本实例测试时有个问题,在录制的时候,会出现buffer over。缓存泄露,待解决。

 

 

[java] view plaincopy
  1. package demo.camera;  
  2. import java.io.BufferedInputStream;  
  3. import java.io.BufferedOutputStream;  
  4. import java.io.DataInputStream;  
  5. import java.io.DataOutputStream;  
  6. import java.io.File;  
  7. import java.io.FileInputStream;  
  8. import java.io.FileOutputStream;  
  9. import java.io.IOException;  
  10. import android.app.Activity;  
  11. import android.content.ContentValues;  
  12. import android.content.Intent;  
  13. import android.hardware.Camera.AutoFocusCallback;  
  14. import android.media.AudioFormat;  
  15. import android.media.AudioManager;  
  16. import android.media.AudioRecord;  
  17. import android.media.AudioTrack;  
  18. import android.media.MediaPlayer;  
  19. import android.media.MediaRecorder;  
  20. import android.net.Uri;  
  21. import android.os.AsyncTask;  
  22. import android.os.Bundle;  
  23. import android.os.Environment;  
  24. import android.provider.MediaStore;  
  25. import android.util.Log;  
  26. import android.view.View;  
  27. import android.widget.Button;  
  28. import android.widget.TextView;  
  29. /** 
  30.  * 该实例中,我们使用AudioRecord类来完成我们的音频录制程序 
  31.  * AudioRecord类,我们可以使用三种不同的read方法来完成录制工作, 
  32.  * 每种方法都有其实用的场合 
  33.  * 一、实例化一个AudioRecord类我们需要传入几种参数 
  34.  * 1、AudioSource:这里可以是MediaRecorder.AudioSource.MIC 
  35.  * 2、SampleRateInHz:录制频率,可以为8000hz或者11025hz等,不同的硬件设备这个值不同 
  36.  * 3、ChannelConfig:录制通道,可以为AudioFormat.CHANNEL_CONFIGURATION_MONO和AudioFormat.CHANNEL_CONFIGURATION_STEREO 
  37.  * 4、AudioFormat:录制编码格式,可以为AudioFormat.ENCODING_16BIT和8BIT,其中16BIT的仿真性比8BIT好,但是需要消耗更多的电量和存储空间 
  38.  * 5、BufferSize:录制缓冲大小:可以通过getMinBufferSize来获取 
  39.  * 这样我们就可以实例化一个AudioRecord对象了 
  40.  * 二、创建一个文件,用于保存录制的内容 
  41.  * 同上篇 
  42.  * 三、打开一个输出流,指向创建的文件 
  43.  * DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))) 
  44.  * 四、现在就可以开始录制了,我们需要创建一个字节数组来存储从AudioRecorder中返回的音频数据,但是 
  45.  * 注意,我们定义的数组要小于定义AudioRecord时指定的那个BufferSize 
  46.  * short[]buffer = new short[BufferSize/4]; 
  47.  * startRecording(); 
  48.  * 然后一个循环,调用AudioRecord的read方法实现读取 
  49.  * 另外使用MediaPlayer是无法播放使用AudioRecord录制的音频的,为了实现播放,我们需要 
  50.  * 使用AudioTrack类来实现 
  51.  * AudioTrack类允许我们播放原始的音频数据 
  52.  *  
  53.  *  
  54.  * 一、实例化一个AudioTrack同样要传入几个参数 
  55.  * 1、StreamType:在AudioManager中有几个常量,其中一个是STREAM_MUSIC; 
  56.  * 2、SampleRateInHz:最好和AudioRecord使用的是同一个值 
  57.  * 3、ChannelConfig:同上 
  58.  * 4、AudioFormat:同上 
  59.  * 5、BufferSize:通过AudioTrack的静态方法getMinBufferSize来获取 
  60.  * 6、Mode:可以是AudioTrack.MODE_STREAM和MODE_STATIC,关于这两种不同之处,可以查阅文档 
  61.  * 二、打开一个输入流,指向刚刚录制内容保存的文件,然后开始播放,边读取边播放 
  62.  *  
  63.  * 实现时,音频的录制和播放分别使用两个AsyncTask来完成  
  64.  */  
  65. public class MyAudioRecord2 extends Activity{  
  66.       
  67.     private TextView stateView;  
  68.       
  69.     private Button btnStart,btnStop,btnPlay,btnFinish;  
  70.       
  71.     private RecordTask recorder;  
  72.     private PlayTask player;  
  73.       
  74.     private File audioFile;  
  75.       
  76.     private boolean isRecording=true, isPlaying=false//标记  
  77.       
  78.     private int frequence = 8000//录制频率,单位hz.这里的值注意了,写的不好,可能实例化AudioRecord对象的时候,会出错。我开始写成11025就不行。这取决于硬件设备  
  79.     private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;  
  80.     private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;  
  81.       
  82.       
  83.     public void onCreate(Bundle savedInstanceState){  
  84.         super.onCreate(savedInstanceState);  
  85.         setContentView(R.layout.my_audio_record);  
  86.           
  87.         stateView = (TextView)this.findViewById(R.id.view_state);  
  88.         stateView.setText("准备开始");  
  89.         btnStart = (Button)this.findViewById(R.id.btn_start);  
  90.         btnStop = (Button)this.findViewById(R.id.btn_stop);  
  91.         btnPlay = (Button)this.findViewById(R.id.btn_play);  
  92.         btnFinish = (Button)this.findViewById(R.id.btn_finish);  
  93.         btnFinish.setText("停止播放");  
  94.         btnStop.setEnabled(false);  
  95.         btnPlay.setEnabled(false);  
  96.         btnFinish.setEnabled(false);  
  97.           
  98.         //在这里我们创建一个文件,用于保存录制内容  
  99.         File fpath = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/data/files/");  
  100.         fpath.mkdirs();//创建文件夹  
  101.         try {  
  102.             //创建临时文件,注意这里的格式为.pcm  
  103.             audioFile = File.createTempFile("recording"".pcm", fpath);  
  104.         } catch (IOException e) {  
  105.             // TODO Auto-generated catch block  
  106.             e.printStackTrace();  
  107.         }         
  108.     }  
  109.       
  110.       
  111.     public void onClick(View v){  
  112.         int id = v.getId();  
  113.         switch(id){  
  114.         case R.id.btn_start:  
  115.             //开始录制  
  116.               
  117.             //这里启动录制任务  
  118.             recorder = new RecordTask();  
  119.             recorder.execute();  
  120.               
  121.             break;  
  122.         case R.id.btn_stop:  
  123.             //停止录制  
  124.             this.isRecording = false;  
  125.             //更新状态  
  126.             //在录制完成时设置,在RecordTask的onPostExecute中完成  
  127.             break;  
  128.         case R.id.btn_play:  
  129.               
  130.             player = new PlayTask();  
  131.             player.execute();  
  132.             break;  
  133.         case R.id.btn_finish:  
  134.             //完成播放  
  135.             this.isPlaying = false;  
  136.             break;  
  137.               
  138.         }  
  139.     }  
  140.       
  141.     class RecordTask extends AsyncTask<Void, Integer, Void>{  
  142.         @Override  
  143.         protected Void doInBackground(Void... arg0) {  
  144.             isRecording = true;  
  145.             try {  
  146.                 //开通输出流到指定的文件  
  147.                 DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(audioFile)));  
  148.                 //根据定义好的几个配置,来获取合适的缓冲大小  
  149.                 int bufferSize = AudioRecord.getMinBufferSize(frequence, channelConfig, audioEncoding);  
  150.                 //实例化AudioRecord  
  151.                 AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.MIC, frequence, channelConfig, audioEncoding, bufferSize);  
  152.                 //定义缓冲  
  153.                 short[] buffer = new short[bufferSize];  
  154.                   
  155.                 //开始录制  
  156.                 record.startRecording();  
  157.                   
  158.                 int r = 0//存储录制进度  
  159.                 //定义循环,根据isRecording的值来判断是否继续录制  
  160.                 while(isRecording){  
  161.                     //从bufferSize中读取字节,返回读取的short个数  
  162.                     //这里老是出现buffer overflow,不知道是什么原因,试了好几个值,都没用,TODO:待解决  
  163.                     int bufferReadResult = record.read(buffer, 0, buffer.length);  
  164.                     //循环将buffer中的音频数据写入到OutputStream中  
  165.                     for(int i=0; i<bufferReadResult; i++){  
  166.                         dos.writeShort(buffer[i]);  
  167.                     }  
  168.                     publishProgress(new Integer(r)); //向UI线程报告当前进度  
  169.                     r++; //自增进度值  
  170.                 }  
  171.                 //录制结束  
  172.                 record.stop();  
  173.                 Log.v("The DOS available:""::"+audioFile.length());  
  174.                 dos.close();  
  175.             } catch (Exception e) {  
  176.                 // TODO: handle exception  
  177.             }  
  178.             return null;  
  179.         }  
  180.           
  181.         //当在上面方法中调用publishProgress时,该方法触发,该方法在UI线程中被执行  
  182.         protected void onProgressUpdate(Integer...progress){  
  183.             stateView.setText(progress[0].toString());  
  184.         }  
  185.           
  186.         protected void onPostExecute(Void result){  
  187.             btnStop.setEnabled(false);  
  188.             btnStart.setEnabled(true);  
  189.             btnPlay.setEnabled(true);  
  190.             btnFinish.setEnabled(false);  
  191.         }  
  192.           
  193.         protected void onPreExecute(){  
  194.             //stateView.setText("正在录制");  
  195.             btnStart.setEnabled(false);  
  196.             btnPlay.setEnabled(false);  
  197.             btnFinish.setEnabled(false);  
  198.             btnStop.setEnabled(true);         
  199.         }  
  200.           
  201.     }  
  202.       
  203.     class PlayTask extends AsyncTask<Void, Integer, Void>{  
  204.         @Override  
  205.         protected Void doInBackground(Void... arg0) {  
  206.             isPlaying = true;  
  207.             int bufferSize = AudioTrack.getMinBufferSize(frequence, channelConfig, audioEncoding);  
  208.             short[] buffer = new short[bufferSize/4];  
  209.             try {  
  210.                 //定义输入流,将音频写入到AudioTrack类中,实现播放  
  211.                 DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(audioFile)));  
  212.                 //实例AudioTrack  
  213.                 AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, frequence, channelConfig, audioEncoding, bufferSize, AudioTrack.MODE_STREAM);  
  214.                 //开始播放  
  215.                 track.play();  
  216.                 //由于AudioTrack播放的是流,所以,我们需要一边播放一边读取  
  217.                 while(isPlaying && dis.available()>0){  
  218.                     int i = 0;  
  219.                     while(dis.available()>0 && i<buffer.length){  
  220.                         buffer[i] = dis.readShort();  
  221.                         i++;  
  222.                     }  
  223.                     //然后将数据写入到AudioTrack中  
  224.                     track.write(buffer, 0, buffer.length);  
  225.                       
  226.                 }  
  227.                   
  228.                 //播放结束  
  229.                 track.stop();  
  230.                 dis.close();  
  231.             } catch (Exception e) {  
  232.                 // TODO: handle exception  
  233.             }  
  234.             return null;  
  235.         }  
  236.           
  237.         protected void onPostExecute(Void result){  
  238.             btnPlay.setEnabled(true);  
  239.             btnFinish.setEnabled(false);  
  240.             btnStart.setEnabled(true);  
  241.             btnStop.setEnabled(false);  
  242.         }  
  243.           
  244.         protected void onPreExecute(){    
  245.               
  246.             //stateView.setText("正在播放");  
  247.             btnStart.setEnabled(false);  
  248.             btnStop.setEnabled(false);  
  249.             btnPlay.setEnabled(false);  
  250.             btnFinish.setEnabled(true);           
  251.         }  
  252.           
  253.     }  
  254. }  

0 0