Audio的系统结构
摘要:Audio系统负责Android中的PCM数据的录制输入流和播放输出流的传输和控制,以及音频设备的管理和设置。这里主要介绍播放和录制环节在各个层次的内容,整个结构层次分明,包括了java接口层,JNI层,本地框架层,audio服务层,硬件抽象层等5层。它的结构图如下
图1-1 Audio系统结构
一、java接口层
AudioManager:音频管理对外的接口,提供了音量和ringtone模式的管理,由getSystemService(Context.AUDIO_SERVICE)返回。
Audioservice:是一个非常重要的java层的系统服务,所有的用户发起的调用都是由它往底层转发的。
AudioSystem:提供管理native接口,只时提供在media包的AudioService内部使用,不对用户直接提供接口。
AudioTrack:提供用户从java层直接输出pcm数据的接口write函数,以及部分播放控制函数。
AudioRecord:提供用户在java层直接从外部获取pcm数据的接口read函数。
下面贴一段边录边播放的例子代码说明这些函数的使用
1 package test.Record; 2 3 import java.io.BufferedInputStream; 4 import java.io.File; 5 import java.io.FileInputStream; 6 import java.io.FileOutputStream; 7 import java.io.InputStream; 8 import java.io.OutputStream; 9 10 import android.app.Activity; 11 import android.content.Intent; 12 import android.media.AudioFormat; 13 import android.media.AudioManager; 14 import android.media.AudioRecord; 15 import android.media.AudioTrack; 16 import android.media.MediaRecorder; 17 import android.os.Bundle; 18 import android.util.Log; 19 import android.view.View; 20 import android.widget.Button; 21 import android.widget.SeekBar; 22 import android.widget.Toast; 23 24 public class testRecord extends Activity { 25 /** Called when the activity is first created. */ 26 Button btnRecord, btnStop, btnExit; 27 SeekBar skbVolume;//调节音量 28 boolean isRecording = false;//是否录放的标记 29 static final int frequency = 44100; 30 static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; 31 static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; 32 int recBufSize,playBufSize; 33 AudioRecord audioRecord; 34 AudioTrack audioTrack; 35 36 @Override 37 public void onCreate(Bundle savedInstanceState) { 38 super.onCreate(savedInstanceState); 39 setContentView(R.layout.main); 40 setTitle("助听器"); 41 42 //------------------------------------------ 43 btnRecord = (Button) this.findViewById(R.id.btnRecord); 44 btnRecord.setOnClickListener(new ClickEvent()); 45 btnStop = (Button) this.findViewById(R.id.btnStop); 46 btnStop.setOnClickListener(new ClickEvent()); 47 btnExit = (Button) this.findViewById(R.id.btnExit); 48 btnExit.setOnClickListener(new ClickEvent()); 49 skbVolume=(SeekBar)this.findViewById(R.id.skbVolume); 50 skbVolume.setMax(100);//音量调节的极限 51 skbVolume.setProgress(100);//设置seekbar的位置值 52 //audioTrack.setStereoVolume(1.0f, 1.0f);//设置当前音量大小 53 skbVolume.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 54 55 56 public void onStopTrackingTouch(SeekBar seekBar) { 57 float vol=(float)(seekBar.getProgress())/(float)(seekBar.getMax()); 58 //audioTrack.setStereoVolume(vol, vol);//设置音量 59 } 60 61 62 public void onStartTrackingTouch(SeekBar seekBar) { 63 // TODO Auto-generated method stub 64 } 65 66 67 public void onProgressChanged(SeekBar seekBar, int progress, 68 boolean fromUser) { 69 // TODO Auto-generated method stub 70 } 71 }); 72 } 73 74 @Override 75 protected void onDestroy() { 76 super.onDestroy(); 77 android.os.Process.killProcess(android.os.Process.myPid()); 78 } 79 80 class ClickEvent implements View.OnClickListener { 81 82 83 public void onClick(View v) { 84 if (v == btnRecord) { 85 if(!isRecording) { 86 isRecording = true; 87 AudioManager audioManager = (AudioManager) getSystemService(getApplicationContext().AUDIO_SERVICE); 88 audioManager.setMode(AudioManager.MODE_NORMAL); 89 recBufSize = AudioRecord.getMinBufferSize(frequency, 90 channelConfiguration, audioEncoding); 91 playBufSize=AudioTrack.getMinBufferSize(frequency, 92 channelConfiguration, audioEncoding); 93 94 audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, 95 channelConfiguration, audioEncoding, recBufSize); 96 97 audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequency, 98 channelConfiguration, audioEncoding, 99 playBufSize, AudioTrack.MODE_STREAM); 100 101 new RecordPlayThread().start();// 开一条线程边录边放 102 } 103 } else if (v == btnStop) { 104 if(isRecording) { 105 isRecording = false; 106 AudioManager audioManager = (AudioManager) getSystemService(getApplicationContext().AUDIO_SERVICE); 107 audioManager.setMode(AudioManager.MODE_NORMAL); 108 } 109 } else if (v == btnExit) { 110 isRecording = false; 111 AudioManager audioManager = (AudioManager) getSystemService(getApplicationContext().AUDIO_SERVICE); 112 audioManager.setMode(AudioManager.MODE_NORMAL); 113 testRecord.this.finish(); 114 } 115 } 116 } 117 118 class RecordPlayThread extends Thread { 119 public void run() { 120 try { 121 byte[] buffer = new byte[recBufSize]; 122 audioRecord.startRecording();//开始录制 123 audioTrack.play();//开始播放 124 while (isRecording) { 125 //从MIC保存数据到缓冲区 126 int bufferReadResult = audioRecord.read(buffer, 0, 127 recBufSize); 128 byte[] tmpBuf = new byte[bufferReadResult]; 129 System.arraycopy(buffer, 0, tmpBuf, 0, bufferReadResult); 130 //写入数据即播放 131 audioTrack.write(tmpBuf, 0, tmpBuf.length); 132 } 133 audioTrack.stop(); 134 audioTrack.release(); 135 audioRecord.stop(); 136 audioRecord.release(); 137 } catch (Throwable t) { 138 Toast.makeText(testRecord.this, t.getMessage(), 1000); 139 } 140 } 141 } 142 }
这些java class接口类在android.media包中,源码目录:frameworks/base/media/java/android/media。这些接口为使用media包的用户提供了音量和路由设置,播放和录制的pcm数据的接口。
二、JNI层
(android_media_AudioSystem, android_audio_AudioTrack, android_audio_AudioRecord),在libandroid_runtime.so包中
三、本地框架层
AudioSystem:media库提供给上层的audio管理的接口,它的实现主要在audiopolicymanger和audioflinger中
AudioTrack:放音部分对上层的接口,stagefright部分也是调用该接口创建和控制playback track
AudioRecord:录音部分对上层的接口,stagefright部分也是调用该接口创建一路record track
IAudioTrack, IAudioRecord, IAudioFlinger:这三个是声明需要底层audioflinger实现的接口函数
这些c接口类在libmedia.so库中,源码目录:frameworks/av/media/libmedia
四、audio服务层
AudioFlinger:这一层主要实现了track的创建,Android层共享内存的分配,多路混音等
五、硬件抽象层
AudioHardwareInterface:这一层需要根据不同的硬件由厂商自己实现,如Primary,Usb,spdif,a2dp等,每一种硬件设备需要继承audioHardwareInterface,实现一个控制硬件so库。主要的类有AudioStreamOut和AudioStreamIn分别是audio输出环节和输入环节,负责write数据流到硬件和从硬件read数据流。
六、总结
通过对各层一个概括性的介绍,对Audio系统的系统结构和源码分布有一个清楚的理解。