http://www.embeddedworld.co.kr/atl/view.asp?a_id=5820



11월호에서는 안드로이드에서 시스템의 사운드를 담당하는 사운드 시스템에 대해 살펴보도록 하겠다. 사운드 시스템은 디스플레이를 담당하는 프레임 버퍼 드라이버와 입력장치와 더불어 시스템에 중요한 역할을 담당하는 드라이버이며, 또한 복잡도가 높은 드라이버 및 구조를 가지고 있다. 복잡함의 가장 큰 원인은 효과적으로 소리를 출력하고, 시스템 사운드와 여러 미디어 플레이어에서 출력되는 사운드를 합성 혹은 믹싱(mixing)하여 출력해 줘야 하고 마이크를 통한 입력, 폰의 경우에는 다양한 출력 형태를 제공해 줘야 하기 때문이다. 사실 사운드 시스템까지 다루면 기본적인 안드로이드 시스템의 구조에 대해 파악했다고 봐도 된다. 물론 계속 진화하는 안드로이드 시스템을 이해하기 위해서는 배워야 할 것들이 많아지고 있다는 함정은 있다. 지금부터 안드로이드 사운드 시스템의 기초가 되는 ALSA부터 안드로이드 사운드 시스템에 대해 살펴보도록 하겠다.


연재차례


1. 안드로이드 시스템의 역사 및 동향 8. Event 시스템의 이해
2. 안드로이드 부팅 분석 9. 안드로이드의 sound 시스템
3. 안드로이드 플랫폼의 이해 10. 안드로이드 Camera
4. 안드로이드의 바인더 11. 안드로이드 Multimedia Framework
5. 안드로이드의 서비스 12. 안드로이드 Power Management
6. 안드로이드의 디스플레이 드라이버와 Surface Flinger 13. 안드로이드 Rooting & Hacking
7. 안드로이드 User Interface


라영호 | ratharn@naver.com
국내 스마트폰의 초창기인 Cellvic에서부터 스마트폰을 개발하였고 윈도 모바일 및 다양한 임베디드 시스템을 개발하고 있다. 현재는 안드로이드 시스템 포팅 및 임베디드 시스템 개발, 컨설팅, 교육 등을 진행하는 회사를 운영하고 있다. 개인 블로그 (www.embeddedce.com )를 통해 임베디드 시스템개발에 대한 다양한 생각과 방법론을 함께 생각해 보고자 노력 중이다.


ALSA (Advanced Linux Sound Architecture)


ALSA는 여러 응용프로그램으로부터의 사운드 데이터를 믹싱(mixing)할 수 있는 인터페이스를 제공하는 구조로 만들어졌다. 기존 리눅스 시스템 구조에 향상된 오디오 기능을 부여하게 된다. 관련 장치 파일들은 /dev/snd에 있으나 직접 엑세스 할 필요는 없다. alsa-lib를 통하여 ALSA의 모든 기능을 이용할 수 있는 구조로 되어 있다. 또한 관련 장치에 대한 제어도 이미 준비되어 있는 유틸리티들을 이용하는 구조로 되어 있다.(alsa-utils)
amixer(console버전)
alsamixer(GUI버전)


ALSA 시스템 구조
리눅스 커널 kernel 소스의 sound/soc/*를 보면 된다. 근래는 전체 구조 중 SoC에 해당하는 부분의 architecture부분을 분리해놨다.



[그림 1] ALSA 구조 그림


ALSA의 경우는 기본 2.4때부터 존재하던 기존의 application 호환성을 위하여 OSS architecture에 호환모드를 제공하도록 설정할 수 있다. OSS emulation는 하위 호환성을 위해 존재하는 계층이며 OSS 장치 파일 엑세스 기능 형태로 기능을 제공하고 있다. 현재도 OSS를 사용하는 응용 프로그램 때문에 호환성을 유지하고 있다. 리눅스 커널 버전 2.6부터 기본 탑재하고 있다.


ALSA 시스템의 주요 기능
Control 인터페이스  - 가능한 장치들을 알아보고 사운드 카드의 레지스터들을 관리하기 위한 일반적인 목적(general-purpose)을 위한 기능
PCM 인터페이스  - 일반적인 디지털 오디오 어플리케이션이 이용하는 디지털 오디오 캡쳐와 출력을 위한 인터페이스.
Raw MIDI 인터페이스  - 전자 음악 장비의 표준인 MIDI(Musical Instrument Digital Interface) 지원, API는 사운드 카드의 MIDI 버스로 접근을 제공. 미디 이벤트와 직접 동작하고 프로그래머는 프로토콜과 타이밍에 대한 관리
Time 인터페이스  - 사운드 이벤트의 동기화에 사용되는 사운드 카드의 하드웨어 타이밍의 접근 제공
Sequencer 인터페이스  - 미디 프로그래밍을 위한 고수준 인터페이스로 보다 많은 미디 프로토콜과 타이밍을 관리
Mixer 인터페이스  - 사운드 볼륨의 제어와 입력 시그널의 선택과 사운드 카드의 장비 제어, 컨트롤 인터페이스의 제일 위에 위치



[그림 2] ALSA 적용된 리눅스 커널의 구조


ALSA API
응용프로그램에서 ALSA는 ALSA Lib에서 제공하는 API를 이용하여 접근하도록 기능을 제공하고 있다. ALSA 라이브러리 API를 기술한 링크는 다음과 같다.


http://www.alsa-project.org/alsa-doc/alsa-lib/
http://www.alsa-project.org/~tiwai/alsa-driver-api/index.html
http://alsa.opensrc.org/index.php/Asynchronous_Playback_(Howto )


ALSA API 기본 사용법
Application에서 ALSA API를 사용하는 가장 기본적인 사용법은 다음과 같다.


1. Device를 연다.
2. H/W Parameter를 설정한다.
3. Access type을 설정한다.
4. 입력할 오디오 포맷을 설정한다.
5. 재생할 샘플링 레이트를 설정한다.
6. 오디오 디바이스를 준비한다.
7. 오디오 디바이스에 데이터를 쓴다.
8. 오디오 디바이스를 닫는다.


주요 ALSA API
snd_pcm_open() - ALSA 장치를 열 때 사용한다.


Name
∨ int snd_pcm_open  (snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode)
∨ PCM 장치 열기
Parameters
∨ pcmp : Returned PCM handle
∨ name : ASCII identifier of the PCM handle
∨ stream : Wanted stream
∨ mode : Open mode (see
  SND_PCM_NONBLOCK,  SND_PCM_ASYNC)
Returns:
∨ 0 on success otherwise a negative error code
Examples:
∨ /test/latency.c,/test/pcm.c, and /test/pcm_min.c.
snd_pcm_hw_params_malloc() - PCM 오디오 HW에 대한 파라메터 설정


Name
∨snd_pcm_hw_params_malloc(snd_pcm_hw_  params_t  *params)
∨ 스택에 snd_pcm_hw_params_t 자료구조 할당
Parameters
∨  snd_pcm_hw_params_t : PCM 오디오 파라메터를 저장하기 위한 구조체
Returns:
∨ 0 on success otherwise a negative error code
Examples:
∨/test/latency.c,/test/pcm.c,and /test/pcm_min.c.


snd_pcm_hw_params_any() - PCM 오디오 HW에 대한 파라메터 설정


Name
∨ snd_pcm_hw_params_any(snd_pcm_t *handle, snd_pcm_hw_params_t *params)
∨ ALSA 관련 각종 파라미터를 초기화
Parameters
∨ snd_pcm_t *handle : ALSA 오디오 H/W 장치제어 핸들
∨ snd_pcm_hw_params_t : PCM 오디오 파라메터를 저장하기 위한 구조체
Returns:
∨ 0 on success otherwise a negative error code
Examples:
∨ /test/latency.c,/test/pcm.c,and /test/pcm_min.c.


snd_pcm_hw_params_set_access()


Name
∨ snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS)
  - ALSA  사운드 하드웨어 접근 타입을 설정
Parameters
∨ SND_PCM_ACCESS
  - SND_PCM_ACCESS_MMAP_INTERLEA
  VED
  ·mmap access with simple interleaved channels
  ·LL RR LL RR LL RR LL RR LL RR LL RR LL RR LL RR LL RR LL RR ...
  - SND_PCM_ACCESS_MMAP_NONINTE RLEAVED
  ·mmap access with simple non interleaved channels
  ·LL LL LL LL LL RR RR RR RR RR LL LL LL LL LL RR RR RR RR RR ...
  - SND_PCM_ACCESS_MMAP_COMPLEX
  ·mmap access with complex placement
  - SND_PCM_ACCESS_RW_INTERLEAVED
  ·snd_pcm_readi/snd_pcm_writei access
  - SND_PCM_ACCESS_RW_NONINTERLE
  AVED
  ·snd_pcm_readn/snd_pcm_writen acce


snd_pcm_hw_params_set_format()
Name
∨snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT)
  - ALSA  사운드를 재생할 PCM 포맷을 설정
  - Parameters
  - SND_PCM_FORMAT
  - SND_PCM_FORMAT_UNKNOWN : Unknown
  - SND_PCM_FORMAT_S8  : Signed 8 bit
  - SND_PCM_FORMAT_U8  : Unsigned 8 bit
  - SND_PCM_FORMAT_S16_LE  : Signed 16 bit Little Endian
  - SND_PCM_FORMAT_S16_BE  : Signed 16 bit Big Endian
  - SND_PCM_FORMAT_U16_LE  : Unsigned 16 bit Little Endian
  - SND_PCM_FORMAT_U16_BE  : Unsigned 16 bit Big Endian


snd_pcm_hw_params_set_rate()


Name
∨ snd_pcm_hw_params_set_rate(handle, params, &val)
∨ snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir)
  - ALSA  사운드를 재생할 PCM 샘플링 레이트를 설정
예: 44.1Khz로 샘플링 레이트를 설정할 경우
∨ snd_pcm_hw_params_set_rate_near(handle, params, 44100, NULL);
∨ snd_pcm_hw_params_set_rate (handle, params, 44100);
∨ Name
∨ snd_pcm_hw_params_set_channels(handle, params, CHANNEL)
  - ALSA 사운드를 스테레오/모노 모드로 재생할 것인지에 대한 선택
Parameters
∨CHANNEL
  - 1 : 모노 / 2 : 스테레오


ALSA API를 사용한 사운드 재생 샘플 프로그램


#include 
void main()
{
 printf("thread_function_start1=\n");
 long loops;
 int rc;
 int size;
 snd_pcm_t *handle;
 snd_pcm_hw_params_t *params;
 unsigned int val;
 int dir;
 snd_pcm_uframes_t frames;
 char *buffer;
 // PCM 재생을 위한 PCM 장치를 연다.
 rc = snd_pcm_open(&handle, "default ", SND_PCM_STREAM_PLAYBACK, 0);
 if (rc < 0) {
 fprintf(stderr, "1unable to open pcm device: %s\n", snd_strerror(rc));
 exit(1);
 }
//스택에 snd_pcm_hw_param_t 자료구조를 할당
 if((rc = snd_pcm_hw_params_malloc (¶ms)) < 0) {
 printf("cannot allocate hardware parameter structure \n");
 }
 // hwparam를 초기화
 if( (rc = snd_pcm_hw_params_any(handle, params)) < 0) {
 printf("cannot initialize
 hardware parameter structure\n");
 }
 // 액세스 타입을 설정
 if( (rc = snd_pcm_hw_params_set_ access(handle, params,
  SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
  printf("cannot set access type \n");
 }
 // 지금 출력할 파일이 어떤 형식인지 알아야 한다. 8비트인지, 16비트인지,
44100인지 24000인지 설정
 // 파일의 오른쪽 버튼으로 속성을 확인한 후 값을 넣어주면 된다.
 // 예: 44100 bits/second sampling rate (CD quality)


 // Signed 16-bit little-endian format
 if( (rc = snd_pcm_hw_params_set_format (handle, params,
 SND_PCM_FORMAT_U16_LE)) < 0) {
 printf("cannot set sample format1 \n");
 }
val = 44100 ;
 if( (rc = snd_pcm_hw_params_set_rate_ near(handle, params, &val, &dir)) < 0) {
 printf("cannot set sample rate1 \n");
 }
 // stereo라면 2이고 mono이면 1이다.
 // Two channels (stereo)
 if( (rc = snd_pcm_hw_params_set_ channels(handle, params, 2)) < 0) {
 printf("cannot set channel count 1\n");
 }
 // Set period size to 32 frames.
 frames = 32;
 if( (rc = snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir)) < 0) {
  printf("cannot snd_pcm_hw_  params_set_period_size_near \n");
 }
 int periods = 2;
 snd_pcm_uframes_t periodsize = 8192;
 snd_pcm_uframes_t buffersize;
 snd_pcm_uframes_t exact_buffersize;
 if( (rc = snd_pcm_hw_params_set_periods (handle,params, periods, 0)) < 0) {
 printf("cannot  snd_pcm_hw_ params_set_periods \n");
 }
 buffersize = (periodsize * periods) >> 2;
 exact_buffersize = buffersize;
 if( (rc = snd_pcm_hw_params_set_ buffer_size_near(handle, params,
  &exact_buffersize)) < 0) {
  printf("cannot snd_pcm_hw_  params_set_buffer_size_near \n");
 }
 if(buffersize !=exact_buffersize ) {
 periodsize = (exact_buffersize << 2 ) / periods;
 }
 //  Write the parameters to the driver  하드웨어 정보를 pcm장치에 적용한다.
 rc = snd_pcm_hw_params(handle, params);
 if (rc < 0) {
 fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
 exit(1);
 }
 buffer = (char *) malloc(periodsize);
 size = periodsize >> 2;
 printf("periodsize===%d\n", periodsize);
 printf("size===%d\n", size);
 // 원하는 파일을 오픈한다.
 int fd;
 if((fd = open ("/home/0202.wav", O_RDONLY)) < 0) {
 printf("file open error");
 }
 ssize_t bytes;
 char buf[2048];
 // 파일을 읽어서 쓴다
 while((bytes = read(fd, buf, size << 2)) >0)
 {
 if((rc = snd_pcm_writei(handle, buf, bytes >> 2)) < 0)
 {
   snd_pcm_prepare(handle);
   printf("buffer underrun1\n");
   }
 }
 snd_pcm_drain(handle);
 snd_pcm_close(handle);
 free(buffer);
 }



[그림 3] Android 사운드 시스템 구조


Android Sound Subsystem의 구조


Android sound subsystem의 class구조와 흐름은 다음과 같다.


Android Sound HAL
Android는 JAVA와 C/C++이 혼합된 구조로 되어 있다. 이에 따라서 각 기능에 따라 계층별로 구분되어 있는 구조이다. 안드로이드 사운드 시스템은 다른 서브시스템과 마찬가지로 2가지 구조로 이루어져 있다.
1) Audio service: Server
2) Audio user: Client


Android Audio Service(server 구성)
audio service를 구성하는 부분은 audioflinger이다. 이 audio service는 init process가 실행되면서init.rc의 내용에 따라 mediaserver를 구동시킨다. mediaserver는 데몬(daemon) 프로세서로 항상 동작대기 상태에 있으며, client의 요청에 따라 특정 동작에 대한 요청을 받아들이고, HAL을 통해서 이를 실행하는 역할을 한다.


Android Audio User(client쪽 구성)
audio client는 JAVA에서의 응용프로그램이 실행되거나 할 때 생성된다(다른 경우는 C/C++로 된 console application이다). sound play의 경우는 AudioTrack class를 생성해서 audioflinger쪽에 서비스를 요청하게 된다. sound record의 경우는 Audio Record class를 생성해서 audioflinger쪽에 서비스를 요청하게 된다. 실제로 모든 서비스의 실행 및 동작은 서버 쪽에서 이루어진다.


Android Sound System 초기화
Android sound system의 초기화는 init process가 init.rc의 내용을 실행시킬 때 mediaserver가 실행되면서 AudioFlinger가 초기화 되면서 시작된다. 시스템의 init 과정에서 AudioFlinger가 생성이 될 때 AudioHardwareInterface가 초기화 된다. 이 AudioHardwareInterface는 Android audio device의 Hardware Abstraction Layer라고 얘기할 수 있다.


초기화 후 Routing 설정
AudioFlinger system 초기화 시 하드웨어의 초기화가 끝나면 audio system의 routing에 대한 정보를 설정하게 된다.
* 출력: 스피커 혹은 헤드셋, Bluetooth등
* 입력: 마이크 혹은 line-in 등
Audio system의 기반이 되는 HAL을 제어하는 AudioFlinger는 다음과 같이 mediaserver daemon이 시작되면서 시작한다.


mediaserver main() 함수
frameworks/base/media/mediaserver/main_mediaserver.cpp


int main(int argc, char** argv)
{
  sp proc(ProcessState::self());
  sp sm = defaultServiceManager();
  LOGI("ServiceManager: %p", sm.get());
  AudioFlinger::instantiate();
  MediaPlayerService::instantiate();
  CameraService::instantiate();  ==>
service 초기화
  ProcessState::self()->startThreadPool();
  IPCThreadState::self()->joinThreadPool();
}


frameworks/base/libs/audioflinger/AudioFlinger.cpp
void AudioFlinger::instantiate() {
  defaultServiceManager()->addService(
  String16("media.audio_flinger"), new AudioFlinger());
}


Android audioflinger의 초기화


AudioFlinger::AudioFlinger()
{
  mHardwareStatus = AUDIO_HW_IDLE;
  mAudioHardware = AudioHardwareInterface::create(); // AudioHardware 생성
  mHardwareStatus = AUDIO_HW_INIT;
  if (mAudioHardware->initCheck() == NO_ERROR) {
  // open 16-bit output stream for s/w mixer
  mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
  status_t status;
  // Audio stream out class생성
  AudioStreamOut *hwOutput = mAudioHardware->openOutputStream (AudioSystem::PCM_16_BIT, 0, 0, &status);
  mHardwareStatus = AUDIO_HW_IDLE;
  if (hwOutput) {
  mHardwareMixerThread = new MixerThread(this, hwOutput, AudioSystem:: AUDIO_OUTPUT_HARDWARE);
  } else {
  LOGE("Failed to initialize hardware output stream, status: %d", status);
  }
...
  // FIXME - this should come from settings,  각종 routing설정
  setRouting(AudioSystem::MODE_NORMAL, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL);
  setRouting(AudioSystem::MODE_RINGTONE, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL);
  setRouting(AudioSystem::MODE_IN_CALL, AudioSystem::ROUTE_EARPIECE, AudioSystem::ROUTE_ALL);
  setMode(AudioSystem::MODE_NORMAL);


  // volume등의 설정
  setMasterVolume(1.0f);
  setMasterMute(false);
...
}
Android audioflinger의 초기화 과정
AudioFlinger의 초기화는 앞의 코드와 같다. AudioFlinger 생성자에서는 AudioHardwareInterface class의 외부 함수인 create()를 호출해서 AudioHard ware를 생성한다. create()함수는 내부적으로 실제 하드웨어가 초기화 될 수 있는 상황일 경우는 초기화를 하고 실제 하드웨어에 대한 포인터를 건네주고 실패했을 경우는 AudioHardwareStub인 dummy device에 대한 포인터를 return하게 된다. 하드웨어 초기화에 성공하면 바로 AudioStreamOut class를 적절한 세팅값을 가지고(실제로는 default로 세팅하게 한다) 생성한다. AudioStream Out이 생성되면 각 입/출력에 대한 routing설정을 하고 volume등의 세팅을 한 후에 초기화 된다.


AudioHardwareInterface
* AudioHardwareInterface::create()
{
  AudioHardwareInterface* hw = 0;
  char value[PROPERTY_VALUE_MAX];


#ifdef GENERIC_AUDIO
  hw = new AudioHardwareGeneric(); // Qualcomm Audio device 초기화
#else
  // if running in emulation - use the emulator driver
  if (property_get("ro.kernel.qemu", value, 0)) {
  LOGD("Running in emulation - using generic audio driver");
  hw = new AudioHardwareGeneric(); // Emulator의 경우는 Qualcomm Audio device 초기화
  }
  else {
  LOGV("Creating Vendor Specific AudioHardware");
  hw = createAudioHardware(); // Qualcomm이외의 Audio device초기화시 호출되는 함수를 이용 초기화
  }
#endif
  if (hw->initCheck() != NO_ERROR) {  // 오디오 하드웨어 준비 체킹
  LOGW("Using stubbed audio hardware. No sound will be produced.");
  delete hw;
  hw = new AudioHardwareStub(); // 실패시 AudioHardwarStub device를 생성(dummy device)
  }
...


  return hw;
}


AudioHardwareInterface의 초기화
컴파일 시 Android의 Board 설정에 따라 Qualcomm audio device를 사용할 것인지 아니면 다른 Audio 하드웨어를 사용할 것인지 GENERIC_AUDIO 라는 define을 가지고 결정한다. 이 GENERIC_AUDIO라는 define은  build/target/board/generic/BoardConfig.mk 파일에BOARD_USES_GENERIC_AUDIO := true 와 같이 정의되어 있을 경우 Qualcomm audio device코드로 컴파일 하게 되어 있다. Qualcomm audio device가 아닐 경우는 일반 Audio 하드웨어 초기화 함수 루틴으로 들어간다. 일반 오디오 하드웨어 루틴으로 들어갈 경우도 현재 kernel이 QEMU 상의 커널일 경우는(즉, emulator일 경우) Qualcomm 하드웨어를 emulation하므로 Qualcomm 오디오 하드웨어로 동작하도록 설정하게 된다. 일반 Audio 하드웨어의 경우는 Qualcomm의 HAL초기화 함수와는 달리(Qualcomm의 경우는 그냥 Class 생성자만 호출) createAudioHardware()란 함수를 호출해서 생성하게 되어 있다. 이런 코드상의 구조 때문에 일반 Hardware의 경우는 createAudioHardware()함수도 HAL작성시 작성해 주어야 한다. Audio Hardware가 생성이 되면 initCheck()를 호출해서 Audio 하드웨어가 사용이 가능한 상태인지 체킹을 하고 실패할 경우는 AudioHardwareStub Dummy device를 생성 성공할 경우는 생성된 하드웨어 포인터를 return하게 된다.


Android sound HAL 포팅시 필요한 class list
Android sound HAL관련 소스는 다음과 같은 파일에 분산되어 있다


hardware/libhardware_legacy/include/hardware_legacy/AudioHardwareInterface.h
hardware/libhardware_legacy/include/hardware_legacy/AudioHardwareBase.h
frameworks/base/libs/audioflinger/AudioHardwareInterface.cpp


위의 파일에서 AudioHardwareBase.h는 AudioHard wareInterface.h의 클래스로부터 상속된 구조이며 실제 Android Audio HAL을 구성하는 AudioHardwareInter face class에 대한 정의는 AudioHardwareInterface.h 에 있다. Audio HAL을 호출하는 실제 코드는 AudioHard wareInterface.cpp에 있으며, 여기서 HAL에 대한 생성, 존재 유/무 체크 등을 처리하게 된다.
Audio HAL을 담당하는 class는 AudioHardwareInter face이며 AudioHardwareBase class는 AudioHard wareInterface class로부터 다음에서 보는 것과 같이 상속이 된 구조이다.
일반적으로 Audio HAL의 경우 이 class로부터 상속된 구조로 선언이 된다.


/**
* AudioHardwareBase is a convenient base class used for implementing the
* AudioHardwareInterface interface.
*/
class AudioHardwareBase :
public AudioHardwareInterface{
public: 
  AudioHardwareBase();
virtual  ~AudioHardwareBase() { };

protected:
  int  mMode;
  uint32_t mRoutes[AudioSystem:: NUM_MODES];
};


Android Sound Subsystem HAL class
Android sound HAL관련 class는 AudioHardware Interface.h 에 정의되어 있는 다음과 같은 세 개의 class이다;
Namespace
namespace android
class AudioHardwareInterface
class AudioStreamOut
class AudioStreamIn


Qualcomm용 audio device가 아닐 경우는 다음과 같은 함수가 반드시 필요하다
AudioHardwareInterface
* createAudioHardware(void);
Qualcomm용의 경우는 AudioHardwareGeneric class에 대한 생성을 직접하고, 그 외의 경우는 createAudio Hardware()를 이용해서 새로운 audio hardware를 초기화 한다


AudioHardwareInterface class의 구조와 Method


class AudioHardwareInterface
{
public:
  virtual status_t  initCheck() = 0;
  virtual status_t setVoiceVolume(float volume) = 0;
  virtual status_t setMasterVolume(float volume) = 0;
  virtual status_t setRouting(int mode, uint32_t routes) = 0;
  virtual status_t getRouting(int mode, uint32_t* routes) = 0;
  virtual status_t  setMode(int mode) = 0;
  virtual status_t  getMode(int* mode) = 0;
  virtual status_t  setMicMute(bool state) = 0;
  virtual status_t getMicMute(bool* state) = 0;
  virtual status_t setParameter(const char* key, const char* value) = 0;
  virtual size_t  getInputBufferSize(uint32_t sampleRate, int format, int channelCount) = 0;
  virtual AudioStreamOut* openOutputStream(
  int format=0,
  int channelCount=0,
  uint32_t sampleRate=0,
  status_t *status=0) = 0;
  virtual AudioStreamIn* openInputStream(
  int format,
  int channelCount,
  uint32_t sampleRate,
  status_t *status,
  AudioSystem::audio_in_acoustics acoustics) = 0;
  static AudioHardwareInterface* create();
protected:
  virtual status_t  doRouting() = 0;
  virtual status_t dump(int fd, const Vector& args) = 0;
};


AudioHardwareInterface class의 구조
AudioHardwareInterface.h는 audio hardware abstraction layer로의 interface를 정의한다. 이 인터페이스는 각종 parameter에 대한 setting과 세팅된 값에 대한 read와, audio routing 경로지정과 입력/출력 stream에 대한 class를 지정한다. AudioFlinger는 audio hardware를 초기화하고, 바로 output stream을 open 하게 된다. 사용자는 이 class의 method를이용하여 audio output 경로를 handset, speaker, Bluetooth 혹은 headset으로 지정할 수 있다. Audio input stream은 AudioFlinger가 record 동작을 수행하기 위하여 호출될 때 초기화 된다.
AudioStreamIn, AudioStreamOut은 AudioHardware Interface class 내부에 선언된 것이 아니라, 외부에 선언이 되어 있으며 AudioHardwareInterface의 내부 메소드 호출에 따라 생성되도록 되어 있다.


끝으로


지금까지 안드로이드 시스템에서 사용하는 사운드 시스템에 대해서 살펴봤다. 다음 호에서는 업데이트된 안드로이드 시스템의 구조를 반영하여 시스템에 대해 좀 더 이해할 수 있도록 내용을 살펴 보도록 하겠다.


<Embedded News>