MediaRecorder有各种可用于音频捕获的其他方法。
getMaxAmplitude:允许请求由MediaPlayer录制的音频的最大振幅。每次调用此方法时都会重置该值,因此每次调用都将返回自从上一次调用以来的最大振幅。可通过定期调用该方法实现音量表。
setMaxDuration:允许以毫秒为单位指定最大录制持续时间。必须在setOutputFormat方法之后和prepare方法之前调用该方法。
setMaxFileSize:允许以字节为单位指定录制的最大文件大小。与setMaxDuration一样,必须在setOutputFormat方法之后和prepare方法之前调用该方法。
以下是之前介绍的定制录音机应用程序的更新,其中包括当前显示的振幅。
1 package com.nthm.androidtestActivity; 2 3 import java.io.File; 4 import java.io.IOException; 5 import com.nthm.androidtest.R; 6 import android.app.Activity; 7 import android.media.MediaPlayer; 8 import android.media.MediaPlayer.OnCompletionListener; 9 import android.media.MediaRecorder; 10 import android.os.AsyncTask; 11 import android.os.Bundle; 12 import android.os.Environment; 13 import android.view.View; 14 import android.view.View.OnClickListener; 15 import android.widget.Button; 16 import android.widget.TextView; 17 18 public class CustomRecorder extends Activity implements OnClickListener, 19 OnCompletionListener {
在这个版本中,添加了一个名为amplitudeTextView的TextView对象,它将显示音频输入的数字振幅。
1 private TextView statusTextView; 2 private TextView amplitudeTextView; 3 private Button startRecording; 4 private Button stopRecording; 5 private Button playRecording; 6 private Button finishButton; 7 private MediaRecorder recorder; 8 private MediaPlayer player; 9 private File audioFile;
需要一个名为RecordAmplitude的新类的实例。这个类是一个内部类,将在本源代码程序清单的末尾对其进行定义。它使用一个名为isRecording的布尔值,当启动MediaRecorder时将该布尔值设置为true。
1 private RecordAmplitude recordAmplitude; 2 private boolean isRecording=false; 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.customrecorder); 7 statusTextView=(TextView) findViewById(R.id.StatusTextView); 8 statusTextView.setText("Ready");
我们将使用TextView来显示所捕获音频的当前振幅。
1 amplitudeTextView=(TextView) findViewById(R.id.AmplitudeTextView); 2 amplitudeTextView.setText("0"); 3 4 stopRecording=(Button) findViewById(R.id.StopRecording); 5 startRecording=(Button) findViewById(R.id.StartRecording); 6 playRecording=(Button) findViewById(R.id.PlayRecording); 7 finishButton=(Button) findViewById(R.id.FinishButton); 8 9 stopRecording.setOnClickListener(this); 10 startRecording.setOnClickListener(this); 11 playRecording.setOnClickListener(this); 12 finishButton.setOnClickListener(this); 13 14 stopRecording.setEnabled(false); 15 playRecording.setEnabled(false); 16 } 17 18 @Override 19 public void onClick(View v) { 20 if(v==finishButton){ 21 finish(); 22 }else if(v==stopRecording){
当完成录制时,设置isRecording布尔值为false,同时调用RecordAmplitude类上的cancel方法。由于RecordAmplitude扩展了AsyncTask,因此在必要时可以用true作为参数,调用cancel以中断它的线程。
1 isRecording=false; 2 recordAmplitude.cancel(true); 3 4 5 recorder.stop(); 6 recorder.release(); 7 player=new MediaPlayer(); 8 player.setOnCompletionListener(this); 9 try { 10 player.setDataSource(audioFile.getAbsolutePath()); 11 } catch (IllegalArgumentException e) { 12 e.printStackTrace(); 13 } catch (SecurityException e) { 14 e.printStackTrace(); 15 } catch (IllegalStateException e) { 16 e.printStackTrace(); 17 } catch (IOException e) { 18 e.printStackTrace(); 19 } 20 try { 21 player.prepare(); 22 } catch (IllegalStateException e) { 23 e.printStackTrace(); 24 } catch (IOException e) { 25 e.printStackTrace(); 26 } 27 statusTextView.setText("Ready to Play"); 28 playRecording.setEnabled(true); 29 stopRecording.setEnabled(false); 30 startRecording.setEnabled(true); 31 }else if(v==startRecording){ 32 recorder=new MediaRecorder(); 33 recorder.setAudioSource(MediaRecorder.AudioSource.MIC); 34 recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 35 recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 36 File path=new File(Environment.getExternalStorageDirectory()+"/ceshi"); 37 path.mkdirs(); 38 try { 39 audioFile=File.createTempFile("recording", ".3gp", path); 40 } catch (IOException e) { 41 e.printStackTrace(); 42 } 43 recorder.setOutputFile(audioFile.getAbsolutePath()); 44 try { 45 recorder.prepare(); 46 } catch (IllegalStateException e) { 47 e.printStackTrace(); 48 } catch (IOException e) { 49 e.printStackTrace(); 50 } 51 recorder.start();
在开始录音之后,设置了isRecording布尔值为true,并创建RecordAmplitude类的新的实例。由于RecordAmplitude扩展了AsyncTask,因此将调用execute方法来启动RecordAmplitude的任务运行。
1 isRecording=true; 2 recordAmplitude=new RecordAmplitude(); 3 recordAmplitude.execute(); 4 5 statusTextView.setText("Recording"); 6 7 playRecording.setEnabled(false); 8 stopRecording.setEnabled(true); 9 startRecording.setEnabled(false); 10 }else if(v==playRecording){ 11 player.start(); 12 statusTextView.setText("Playing"); 13 14 playRecording.setEnabled(false); 15 stopRecording.setEnabled(false); 16 startRecording.setEnabled(false); 17 } 18 } 19 @Override 20 public void onCompletion(MediaPlayer mp) { 21 statusTextView.setText("Ready"); 22 23 playRecording.setEnabled(true); 24 stopRecording.setEnabled(false); 25 startRecording.setEnabled(true); 26 }
以下是RecordAmplitude的定义,其扩展了AsyncTask类,AsyncTask是Android上一个非常好用的工具类,其提供了一个线程,用于执行长期运行的任务,而不用联系用户界面或使应用程序不响应。
1 private class RecordAmplitude extends AsyncTask<Void, Integer, Void>{
doInBackground方法在一个单独的线程上运行,而且只有在对象上调用execute方法时才开始运行。只要isRecording为true,该方法就将一直循环:调用Thread.sleep(500);,使其在半秒钟内不做任何事情;完成该操作之后,将调用publishProgress方法,并传入在MediaRecorder对象上执行getMaxAmplitude方法的结果。
1 @Override 2 protected Void doInBackground(Void... params) { 3 while(isRecording){ 4 try{ 5 Thread.sleep(500); 6 }catch(Exception e){ 7 e.printStackTrace(); 8 } 9 publishProgress(recorder.getMaxAmplitude()); 10 } 11 return null; 12 }
上面对publishProgress的调用下面定义的onProgressUpdate方法,它在主线程上运行,所以可以与用户界面交互。在当前情况下,它会使用从publishProgress方法调用中传递过来的值对amplitudeTextView进行更新。
1 @Override 2 protected void onProgressUpdate(Integer... values) { 3 super.onProgressUpdate(values); 4 amplitudeTextView.setText(values[0].toString()); 5 } 6 } 7 }
当然,需要更新布局XML,以包括用于显示振幅的TextView。
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical" 5 > 6 <TextView 7 android:id="@+id/StatusTextView" 8 android:text="Status" 9 android:layout_width="fill_parent" 10 android:layout_height="wrap_content" 11 android:textSize="35dip"></TextView> 12 <TextView 13 android:id="@+id/AmplitudeTextView" 14 android:text="0" 15 android:layout_width="fill_parent" 16 android:layout_height="wrap_content" 17 android:textSize="35dip"></TextView> 18 <Button 19 android:layout_width="wrap_content" 20 android:layout_height="wrap_content" 21 android:id="@+id/StartRecording" 22 android:text="Start Recording"/> 23 <Button 24 android:layout_width="wrap_content" 25 android:layout_height="wrap_content" 26 android:id="@+id/StopRecording" 27 android:text="Stop Recording"/> 28 <Button 29 android:layout_width="wrap_content" 30 android:layout_height="wrap_content" 31 android:id="@+id/PlayRecording" 32 android:text="Play Recording"/> 33 <Button 34 android:layout_width="wrap_content" 35 android:layout_height="wrap_content" 36 android:id="@+id/FinishButton" 37 android:text="Finish"/> 38 </LinearLayout>
使用AsyncTask可定期做某件事情,因此它是一种当其他事情正在进行时向用户自动提供更新信息的优秀方式。这为MediaPlayer示例提供了良好的用户体验。使用getMaxAmplitude方法可以向用户提供一些与目前正在发生的录制相关的反馈。
Android2.2(Froyo)还提供了以下方法:
setAudioChannels:允许指定将录制的音频通道数。通常是一个通道(单声道)或两个通道(立体声)。必须在prepare方法之前调用该方法。
setAudioEncodingBitRate:允许指定当压缩视频时编码器所使用的每秒位数(位/秒)。必须在prepare方法之前调用该方法。
setAudioSampleRate:允许指定捕获和编码的音频的采样率。硬件和使用的编解码器将会决定合适的采样率。必须在prepare方法之前调用该方法。