오디오 포커스

논리적 스트림을 시작하기 전에 앱은 논리적 스트림에 사용할 오디오 속성과 동일한 오디오 속성을 사용하여 오디오 포커스를 요청해야 합니다. 이러한 포커스 요청을 보내는 것이 좋긴 하지만 시스템에서 이를 강제하지는 않습니다. 일부 앱은 특정 동작을 위해 요청 전송을 명시적으로 건너뛸 수도 있습니다(예: 전화 통화 중에 의도적으로 사운드를 재생하려는 경우).

따라서 포커스는 기본 오디오 제어 메커니즘이 아니라 재생을 간접적으로 제어하고 충돌을 피하는 방법으로 생각되어야 합니다. 예를 들어 차량의 경우 오디오 하위 시스템을 작동시키기 위해 포커스 시스템을 사용하면 안 됩니다.

포커스 상호작용

AAOS의 요구를 지원하기 위해 오디오 포커스 요청은 이 요청의 CarAudioContext와 현재 포커스 보유자 간에 사전 정의된 상호작용을 기반으로 처리됩니다. 상호작용에는 배타, 거부, 동시의 세 가지 유형이 있습니다.

배타적 상호작용

배타적 상호작용에서는 한 번에 한 애플리케이션만 포커스를 보유할 수 있습니다. 따라서 수신되는 포커스 요청에 포커스가 부여되고 기존 포커스 보유자는 포커스를 상실합니다. 기존 애플리케이션에서 이미 음악을 재생 중일 때 사용자가 새 음악 애플리케이션을 시작하는 경우를 예로 들 수 있습니다. 두 애플리케이션이 모두 미디어를 재생하므로 한 번에 한 애플리케이션만 포커스를 보유할 수 있습니다. 따라서 새로 시작된 애플리케이션의 포커스 요청이 AUDIOFOCUS_REQUEST_GRANTED와 함께 반환되고, 현재 음악을 재생 중인 애플리케이션은 요청의 유형에 해당하는 포커스 변경 이벤트를 상실 상태와 함께 수신합니다. 이것이 Android에서 가장 일반적으로 나타나는 상호작용 모델입니다.

거부 상호작용

거부 상호작용에서는 수신되는 요청이 항상 거부됩니다. 통화 진행 중에 음악을 재생하려고 시도하는 경우가 거부 상호작용의 예입니다. 다이얼러가 현재 통화를 위해 오디오 포커스를 보유하고 있고 두 번째 애플리케이션이 음악 재생을 위해 포커스를 요청하는 경우 음악 애플리케이션은 요청의 응답으로 AUDIOFOCUS_REQUEST_FAILED를 수신합니다. 포커스 요청이 거부되므로, 현재 포커스 보유자에게 어떤 유형의 포커스 손실도 전달되지 않습니다.

동시 상호작용

AAOS에서 가장 고유한 기능은 동시 상호작용입니다. 차량에서 오디오 포커스를 요청하는 애플리케이션이 다른 애플리케이션과 동시에 포커스를 보유할 수 있습니다. 동시 상호작용이 발생하려면 다음 조건을 충족해야 합니다.

이러한 기준이 충족되면 포커스 요청이 AUDIOFOCUS_REQUEST_GRANTED와 함께 반환되며 현재 포커스 보유자는 포커스에 변화가 없습니다. 하지만 현재 포커스 보유자가 볼륨 낮추기 이벤트를 수신하도록 선택하거나 볼륨이 낮아졌을 때 일시중지하도록 선택하는 경우에는 배타적 상호작용과 마찬가지로 포커스를 잃게 됩니다.

동시 스트림 처리

동시 상호작용에는 여러 가지 유용한 애플리케이션이 있지만, OEM이 출력 장치의 하드웨어 수준에서 사운드 믹스를 처리하고 볼륨을 낮춰야 합니다. 따라서 CarAudioContext는 동시에 재생할 수 없는 CarAudioContext와 동일한 출력 장치로만 라우팅되는 것이 좋습니다. 동시 스트림을 위한 별도의 출력 장치가 있으면 HAL이 스트림을 믹싱하기 전에 두 스트림 중 하나의 볼륨을 낮추거나 물리적 스트림을 차량의 다른 스피커로 라우팅할 수 있습니다. Android 내에서 논리적 스트림이 믹싱되는 경우 포커스 획득이 변경 없이 동일한 물리적 스트림의 일부로 제공됩니다.

예를 들어 내비게이션과 미디어가 동시에 제공될 때 내비게이션의 안내가 선명하게 들릴 수 있도록 미디어 스트림이 획득한 포커스가 일시적으로 줄어들(볼륨 낮춤) 수 있습니다. 또는 내비게이션 스트림을 운전자 쪽 스피커로 라우팅하고 미디어는 계속 재생하여 차량에 있는 나머지 스피커를 통해 제공할 수도 있습니다.

상호작용 매트릭스

아래 표는 CarAudioService에서 정의된 상호작용 매트릭스를 보여줍니다. 행은 현재 포커스 보유자의 CarAudioContext를 나타내고 열은 수신 요청의 컨텍스트를 나타냅니다.

현재 음악 미디어 앱에 포커스가 있고 내비게이션 앱에서 포커스를 요청하는 예를 보면, 매트릭스는 동시 상호작용의 다른 기준이 충족된다는 가정 하에 두 애플리케이션 상호작용이 동시에 재생될 수 있음을 나타냅니다.

동시 상호작용으로 인해 포커스 보유자가 둘 이상 존재할 수도 있습니다. 이 경우 수신 포커스 요청과 현재 포커스 보유자를 각각 비교하여 적용할 상호작용 유형을 결정합니다. 이 경우 가장 보수적인 상호작용이 우선적으로 적용됩니다(거부, 배타, 동시의 순서로).

다음 표에서는 수신 포커스 요청(열)의 CarAudioContext와 기존 포커스 보유자(행)의 컨텍스트 사이의 포커스 상호작용을 보여줍니다. 각 셀은 두 컨텍스트에 예상되는 상호작용 유형을 나타냅니다.

  • R: 거부 상호작용
  • E: 배타적 상호작용
  • C: 동시 상호작용

오디오 포커스 상호작용

그림 1. 오디오 포커스 상호작용

Android 11에는 사용자가 내비게이션과 전화 통화 간의 상호작용 동작을 변경할 수 있도록 새로운 사용자 설정이 도입되었습니다. 설정되면 android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL은 수신되는 NAVIGATION 포커스 요청과 현재 CALL 포커스 보유자 간의 상호작용을 동시에서 거부로 변경합니다. 따라서 사용자가 내비게이션 안내로 인해 통화를 방해받고 싶지 않다면 이 설정을 사용 설정하면 됩니다. 사용자에게 이 설정이 유지되며, 이후 포커스 요청이 새 설정 값을 따르도록 동적으로 설정할 수 있습니다.

지연 가능한 오디오 포커스

AAOS는 Android 11에서 지연 가능한 오디오 포커스 요청을 지원합니다. 이에 따라, 일시적이지 않은 포커스 요청의 경우 현재 포커스 보유자와의 상호작용의 결과 일반적으로 거부되면 지연할 수 있습니다. 이후 포커스가 변경되어 지연된 요청이 포커스를 획득할 수 있는 상태가 되면 요청한 포커스가 부여됩니다.

지연된 오디오 포커스 요청의 규칙

  • 일시적이지 않은 요청만 해당 - 앞서 언급한 것처럼 지연된 요청은 일시적이지 않은 요청에만 적용할 수 있습니다. 이는 일시적인 요청이 발생한 지 한참 후에 사운드가 재생되지 않도록 하기 위함입니다.
  • 한 번에 요청 하나만 지연할 수 있음 - 지연된 요청이 이미 있는 상황에서 지연 가능한 요청이 발생하는 경우 원래 있던 지연된 요청은 AUDIOFOCUS_LOSS 변경 이벤트를 수신하고 새 요청은 AUDIOFOCUS_REQUEST_DELAYED의 동기 응답을 수신합니다.
  • 지연 가능한 요청에는 OnAudioFocusChangeListener가 있어야 합니다. 요청이 지연되면 리스너는 나중에 요청이 승인(AUDIOFOCUS_GAIN)될 때나 이후에 요청이 거부(AUDIOFOCUS_LOSS)될 때 요청자에 이를 알립니다.

지연 가능한 포커스 요청

지연 가능한 요청을 작성하려면 AudioFocusRequest.Builder#setAcceptsDelayedFocusGain을 사용합니다.

mMediaWithDelayedFocusListener = new MediaWithDelayedFocusListener();

mDelayedFocusRequest = new AudioFocusRequest
     .Builder(AudioManager.AUDIOFOCUS_GAIN)
     .setAudioAttributes(mMusicAudioAttrib)
     .setOnAudioFocusChangeListener(mMediaWithDelayedFocusListener)
     .setForceDucking(false)
     .setWillPauseWhenDucked(false)
     .setAcceptsDelayedFocusGain(true)
     .build();

그런 다음, 요청할 때 AUDIOFOCUS_REQUEST_DELAYED 응답을 처리합니다.

int delayedFocusRequestResults = mAudioManager.requestAudioFocus(mDelayedFocusRequest);
if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// start audio playback
return;
}
if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
     // audio playback delayed to audio focus listener
     return;
}

요청이 지연되면 포커스 리스너가 포커스의 변경사항을 처리합니다.

private final class MediaWithDelayedFocusListener implements
OnAudioFocusChangeListener {
       @Override
       public void onAudioFocusChange(int focusChange) {
           synchronized (mLock) {
               switch (focusChange) {
                   case AudioManager.AUDIOFOCUS_GAIN:
                       … // Start focus playback
                   case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                       … // Pause media transiently
                   case AudioManager.AUDIOFOCUS_LOSS:
                       … // Stop media

다중 영역 포커스 관리

오디오 영역이 여러 개 있는 차량의 경우 오디오 포커스는 영역별로 독립적으로 관리됩니다. 따라서 한 영역에 요청하는 경우 다른 영역의 포커스 보유자를 고려하지 않으며 다른 영역의 포커스 보유자가 포커스를 상실하지도 않습니다. 이를 통해 자동차 실내의 기본 포커스를 뒷좌석 엔터테인먼트 시스템과 별도로 관리할 수 있으므로 한 영역의 포커스 변경으로 인해 다른 영역의 오디오 재생이 중단되지 않습니다.

CarAudioService가 모든 애플리케이션의 포커스 관리를 자동으로 처리합니다. 포커스 요청의 오디오 영역은 연결된 UserId 또는 UID에 따라 결정됩니다. 자세한 내용은 오디오 라우팅을 참고하세요.

여러 영역에서 동시에 오디오 요청

앱이 여러 영역에서 오디오를 동시에 재생하려면 번들에 AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID를 포함하여 각 영역에 포커스를 요청해야 합니다.

//Create attribute with bundle and AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID
Bundle bundle = new Bundle();
bundle.putInt(CarAudioManager.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID,
               zoneId);

AudioAttributes attributesWithZone = new AudioAttributes.Builder()
     .setUsage(AudioAttributes.USAGE_MEDIA)
     .addBundle(bundle)
     .build();

//Create focus request using built attributesWithZone

이 번들 매개변수를 사용하면 요청자가 자동 오디오 영역 매핑을 재정의하여 지정된 영역 ID를 대신 사용할 수 있습니다. 즉, 이 매개변수를 사용하면 앱이 다양한 오디오 영역별로 별도의 요청을 실행할 수 있습니다.

HAL 오디오 포커스

이제 Android 11부터 HAL은 외부 스트림을 대신하여 포커스를 요청할 수 있습니다. 선택사항이지만 이러한 API를 사용하여 외부 사운드가 Android 생태계에 더 활발히 참여하여 보다 원활한 사용자 환경을 제공할 수 있도록 하는 것이 좋습니다.

어느 사운드에 우선순위를 부여할지는 여전히 HAL이 최종적으로 결정한다는 점을 기억하세요. 응급 상황 및 안전에 중요한 사운드는 HAL에 오디오 포커스가 부여되었는지 여부와 상관없이 재생되어야 하며 HAL이 오디오 포커스를 상실하더라도 계속 재생되어야 합니다. 규정에 따라 요구되는 사운드의 경우도 마찬가지입니다.

마찬가지로, HAL은 응급 상황이나 안전에 중요한 사운드가 재생될 때 선명하게 들리도록 적절하게 Android 스트림을 사전에 음소거해야 합니다.

AudioControl@2.0

2.0 버전의 AudioControl HAL에는 몇 가지 새로운 API가 도입되었습니다.

API 목적
IAudioControl#registerFocusListener IFocusListener의 인스턴스를 AudioControl HAL에 등록합니다. 이 리스너를 사용하면 HAL이 오디오 포커스를 요청하고 포기할 수 있습니다. HAL은 Android에서 리스너를 등록 취소하는 데 사용할 ICloseHandle 인스턴스를 제공해야 합니다.
IAudioControl#onAudioFocusChange IFocusListener를 통해 HAL이 실행한 포커스 요청의 상태 변경사항을 HAL에 알립니다. 여기에는 초기 포커스 요청에 관한 응답이 포함됩니다.
IFocusListener#requestAudioFocus 지정된 용도, 영역 ID, 포커스 획득 유형의 경우 HAL을 대신하여 포커스를 요청합니다.
IFocusListener#abandonAudioFocus 지정된 용도 및 영역 ID에 관한 기존 HAL 포커스 요청을 포기합니다.

HAL은 동시에 여러 개의 포커스 요청을 할 수 있지만, 용도-영역 ID의 쌍별 요청 하나씩으로 제한됩니다. Android에서는 요청이 발생하면 HAL에서 용도에 해당하는 사운드를 즉시 재생하기 시작하고 포커스를 포기할 때까지 계속 재생한다고 가정합니다.

registerFocusListener 이외의 모든 요청은 oneway입니다. 이는 포커스 요청이 처리되는 동안 Android가 HAL을 지연시키지 않도록 하기 위함입니다. HAL은 안전에 중요한 사운드를 재생할 때는 포커스를 획득할 때까지 기다려서는 안 됩니다. HAL이 IAudioControl#onAudioFocusChange를 통해 오디오 포커스의 변경사항을 수신 대기하고 이에 응답하는 것은 선택사항이지만 해당하는 경우 권장됩니다.