2012年9月27日星期四

Audio的系统结构

Audio的系统结构

  摘要:Audio系统负责Android中的PCM数据的录制输入流和播放输出流的传输和控制,以及音频设备的管理和设置。这里主要介绍播放和录制环节在各个层次的内容,整个结构层次分明,包括了java接口层,JNI层,本地框架层,audio服务层,硬件抽象层等5层。它的结构图如下
图1-1 Audio系统结构                                                       图1-1 Audio系统结构

  一、java接口层

    AudioManager:音频管理对外的接口,提供了音量和ringtone模式的管理,由getSystemService(Context.AUDIO_SERVICE)返回。
    Audioservice:是一个非常重要的java层的系统服务,所有的用户发起的调用都是由它往底层转发的。
    AudioSystem:提供管理native接口,只时提供在media包的AudioService内部使用,不对用户直接提供接口。
    AudioTrack:提供用户从java层直接输出pcm数据的接口write函数,以及部分播放控制函数。
    AudioRecord:提供用户在java层直接从外部获取pcm数据的接口read函数。
    下面贴一段边录边播放的例子代码说明这些函数的使用

View Code
  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系统的系统结构和源码分布有一个清楚的理解。


TAG: