除了通过意图启动录音机和使用MediaRecorder之外,Android还提供了第三种方法来捕获音频;使用称为AudioRecord的类。AudioRecord是3种方法中最灵活的方法(因为它允许访问原始音频流),但是它拥有最少的内置功能,如不会自动压缩音频。
使用AudioRecord的基础知识非常简单。我们只需要构造一个AudioRecord类型的对象,并传入各种不同的配置参数。
需要指定的第一个值是音频源。下面使用的值与之前用于MediaPlayer的值相同。其在MediaRecorder.AudioSource中定义。实际上,这意味着可以使用MediaRecorder.AudioSource.MIC。
1 int audioSource=MediaRecorder.AudioSource.MIC;
需要指定的下一个值是录制的采样率,应以赫兹(Hz)为单位指定它。我们知道,MediaRecorder采样的音频时8kHz或8000Hz。而CD质量的音频通常是44.1kHz或44100Hz。Hz或赫兹是每秒的样本数量。不同的Android手机硬件将能够以不同的采样率进行采样。对于此处的示例应用程序而言,他将以11025Hz的采样率进行采样,这是另一个常用的采样率。
1 int sampleRateInHz=11025;
接下来,需要指定捕获的音频通道的数量。在AudioFormat类中指定了用于此参数的常量,而且可根据名称理解他们。
AudioFormat.CHANNEL_CONFIGURATION_MONO 单通道
AudioFormat.CHANNEL_CONFIGURATION_STEREO 双通道
错误的音频通道掩码
AudioFormat.CHANNEL_CONFIGURATION_DEFAULT 默认通道
现在讲使用单声道配置。
1 private int channelConfig=AudioFormat.CHANNEL_CONFIGURATION_MONO;
随后,需要指定音频格式。在AudioFormat类中也指定了以下各种可能的常量。
AudioFormat.ENCODING_DEFAULT
AudioFormat.ENCODING_INVALID
AudioFormat.ENCODING_PCM_16BIT
AudioFormat.ENCODING_PCM_8BIT
在这4个选择中,我们可以选择PCM 16位和PCM 8位。PCM代码脉冲编码调制(Pulse Code Modulation),它实际上是原始的音频样本。因此可以设置每个样本的分辨率为16位或者8位。十六位将占用更多的空间和处理能力,但表示的音频将更加接近真实。
在示例中将采用16位的版本。
1 int audioFormat=AudioFormat.ENCODING_PCM_16BIT;
最后将需要指定缓冲区大小。实际上可以查询AudioRecord类以获得最小缓冲区大小,查询方式是调用getMinBufferSize静态方法,同时传入采样率、通道配置以及音频格式。
1 int bufferSizeInBytes=AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
现在可以构造实际的AudioRecord对象。
1 AudioTrack audioRtack=new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
AudioRecord类实际上并不保存捕获的音频,因此需要手动保存捕获的音频。我们可能想要做的第一件事是将音频录制到一个文件中。
为此,需要创建一个文件。
1 File path=new File(Environment.getExternalStorageDirectory().getAbsoluteFile()+"video");
2 path.mkdirs();
3 try{
4 recordingFile=File.createTempFile("recording", ".pcm",path);
5 }catch(Exception e){
6 e.printStackTrace();
7 }
接下来讲创建对应该文件的OutputStream,尤其是出于性能和便利的原因,可以将它包装在BufferedOutputStream和DataOutputStream中。
1 DataOutputStream dos=new DataOutputStream(new BufferedOutputStream(new FileOutputStream(recordingFile)));
现在可以启动捕获,同时将音频样本写入到文件。可以使用short数组来保存从AudioRecord对象读取而来的音频。同时,将采用比AudioRecord对象的缓冲区更小的数组,从而在将音频读出来之前缓冲区没有填满。
为了确保此数组小于缓冲区大小,需要将缓冲区大小除以4。因为缓冲区的大小以字节为单位,而每个short类型的数据占用两个字节,所以除以2并不足够。除以4将使得数组是AudioRecord对象内部缓冲区大小的一半。
1 short[]buffer=new short[bufferSize/4];
只需要调用AudioRecord对象上的startRecording方法即可处理这些事情。
1 audioRecord.startRecording();
录制开始之后可以构造一个循环,不断从AudioRecord对象读取音频并放入short数组中,同时写入对应文件的DataOutputStream。
1 while(true){
2 int bufferReadResult=audioRecord.read(buffer, 0,bufferSize/4);
3 for(int i=0;i<bufferReadResult;i++){
4 dos.writeShort(buffer[i]);
5 }
6 }
7 audioRecord.stop();
8 dos.close();
当完成这些工作之后,调用AudioRecord对象上的stop方法和DataOutputStream上的close方法。
当然,在实际应用中不会将这段代码放在一个while(true)循环中,因为它将永远不会停止。我们可能还想在某个线程中运行它,从而它不会绑定用户界面以及我们希望应用程序在录制时做的其他工作。
由于使用了AudioRecord类来捕获音频,因此在给出完整示例之前先看看如何播放音频。