AAudio

AAudio는 Android O 출시에 도입된 새로운 Android C API입니다. 이는 필요한 지연 시간이 짧은 고성능 오디오 애플리케이션용으로 고안되었습니다. 앱은 스트림에 데이터를 읽고 써서 AAudio와 통신합니다.

AAudio API는 일부러 최소한의 수준으로 만들었으므로 다음과 같은 기능은 실행되지 않습니다.

  • 오디오 기기 열거
  • 오디오 엔드포인트 간 자동 라우팅
  • 파일 I/O
  • 압축 오디오 디코딩
  • 콜백 하나의 모든 입력/스트림의 자동 표시

오디오 스트림

AAudio는 사용자의 앱과 사용자의 Android 기기 오디오 입력 및 출력 사이를 오고 가며 오디오 데이터를 이동합니다. 앱은 오디오 스트림에서 데이터를 읽거나 써서 안팎으로 전달하며 이를 대표하는 것이 AAudioStream 구조입니다. 읽기/쓰기 호출은 블로킹일 수도 있고 비블로킹일 수도 있습니다.

스트림은 다음에 의해 정의됩니다.

  • 스트림 내 데이터의 소스 또는 싱크인 오디오 기기
  • 스트림이 오디오 기기에 대한 독점적 액세스 권한이 있는지 판별하는 공유 모드로, 이러한 권한이 없는 경우 오디오 기기는 복수의 스트림 간에 공유될 수 있음
  • 스트림 내 오디오 데이터의 형식

오디오 기기

각 스트림은 하나의 오디오 기기에 연결되어 있습니다.

오디오 기기란 하드웨어 인터페이스 또는 가상의 엔드포인트를 말하며, 이는 디지털 오디오 데이터의 지속적인 스트림에 관한 소스 또는 싱크 역할을 합니다. 오디오 기기(내장 마이크 또는 블루투스 헤드셋)를 앱을 실행하는 Android 기기(휴대전화 또는 시계)와 혼동해서는 안 됩니다.

AudioManager 메서드 getDevices()를 사용하여 Android 기기에서 사용할 수 있는 오디오 기기를 찾을 수 있습니다. 이 메서드는 각 기기의 type 관련 정보를 반환합니다.

각 오디오 기기에는 Android 기기에서의 고유 ID가 있습니다. 이 ID를 사용하여 오디오 스트림을 특정 오디오 기기에 바인딩할 수 있습니다. 다만 대부분의 경우 사용자가 직접 지정하기보다 AAudio가 기본 설정된 주 기기를 선택하도록 두면 됩니다.

스트림에 연결된 오디오 기기는 스트림이 출력용인지 입력용인지 결정합니다. 스트림은 데이터를 한 방향으로만 옮길 수 있습니다. 스트림을 정의하면 그 방향도 설정하게 됩니다. 스트림을 열면 Android가 오디오 기기와 스트림 방향이 일치하는지 확인합니다.

공유 모드

스트림에는 다음과 같은 공유 모드가 있습니다.

  • AAUDIO_SHARING_MODE_EXCLUSIVE란 스트림이 오디오 기기에 독점 접근할 수 있음을 의미합니다. 이 기기는 다른 오디오 스트림으로 사용할 수 없습니다. 오디오 기기가 이미 사용 중이라면 스트림에 독점 접근이 불가능할 수 있습니다. 독점 스트림은 지연 시간이 느리지만, 연결이 해제되기 쉽습니다. 독점 스트림이 필요하지 않게 되면 바로 종료하여 다른 앱이 해당 기기에 접근할 수 있도록 해야 합니다. 독점 스트림은 가장 짧은 지연 시간을 제공합니다.
  • AAUDIO_SHARING_MODE_SHARED를 통해 AAudio가 오디오를 믹싱할 수 있습니다. AAudio는 동일한 기기에 할당된 모든 공유 스트림을 믹싱합니다.

스트림을 생성할 때 공유 모드를 명시적으로 설정할 수 있습니다. 공유 모드의 기본값은 SHARED입니다.

오디오 형식

스트림을 통해 전달된 데이터는 일반적인 디지털 오디오 속성을 보유합니다. 이는 다음과 같습니다.

  • 샘플 형식
  • 프레임당 샘플
  • 샘플링 레이트

AAudio는 다음과 같은 샘플 형식을 허용합니다.

aaudio_format_t C 데이터 유형 참고
AAUDIO_FORMAT_PCM_I16 int16_t 일반 16비트 샘플, Q0.15 형식
AAUDIO_FORMAT_PCM_FLOAT 부동 소수점 -1.0 ~ +1.0

AAudio는 자체적으로 샘플 변환을 할 수 있습니다. 예를 들어, 앱이 FLOAT 데이터를 작성하지만 HAL에서 PCM_I16을 사용하는 경우 AAudio가 샘플을 자동으로 변환할 수 있습니다. 어느 방향으로든 변환할 수 있습니다. 앱이 오디오 입력을 처리한다면 입력 형식을 확인한 다음 필요한 경우 데이터를 변환할 준비를 하는 것이 좋습니다(다음 예시 참고).

aaudio_format_t dataFormat = AAudioStream_getDataFormat(stream);
//... later
if (dataFormat == AAUDIO_FORMAT_PCM_I16) {
     convertFloatToPcm16(...)
}

오디오 스트림 생성

AAudio 라이브러리는 빌더 디자인 패턴을 따르며, AAudioStreamBuilder를 제공합니다.

  1. AAudioStreamBuilder 생성:

    AAudioStreamBuilder *builder;
    aaudio_result_t result = AAudio_createStreamBuilder(&builder);
    

  2. 스트림 매개변수에 해당하는 빌더 함수를 사용하여 이 빌더에 오디오 스트림 구성을 설정합니다. 다음과 같은 선택적 설정 함수를 이용할 수 있습니다.

    AAudioStreamBuilder_setDeviceId(builder, deviceId);
    AAudioStreamBuilder_setDirection(builder, direction);
    AAudioStreamBuilder_setSharingMode(builder, mode);
    AAudioStreamBuilder_setSampleRate(builder, sampleRate);
    AAudioStreamBuilder_setChannelCount(builder, channelCount);
    AAudioStreamBuilder_setFormat(builder, format);
    AAudioStreamBuilder_setBufferCapacityInFrames(builder, frames);
    

    참고로 이러한 방법은 정의되지 않은 상수 또는 범위를 벗어난 값 등의 오류를 보고하지 않습니다.

    deviceId를 지정하지 않으면 기본값은 주 출력 기기가 됩니다. 스트림 방향을 지정하지 않으면 기본값은 출력 스트림이 됩니다. 모든 기타 매개변수의 값을 명시적으로 설정하거나, 매개변수를 전혀 설정하지 않거나 AAUDIO_UNSPECIFIED로 설정하여 시스템에서 최적의 값을 할당하도록 할 수 있습니다.

    만약을 위해 아래 4단계에서 설명한 것처럼 오디오 스트림을 생성한 후 상태를 확인합니다.

  3. AAudioStreamBuilder가 구성되면 이를 사용하여 스트림을 생성합니다.

    AAudioStream *stream;
    result = AAudioStreamBuilder_openStream(builder, &stream);
    

  4. 스트림을 생성한 후, 구성을 확인합니다. 샘플 형식, 샘플링 레이트 또는 프레임당 샘플을 지정했다면 값은 변하지 않습니다. 공유 모드 또는 버퍼 용량을 지정했다면 스트림의 오디오 기기 및 이를 실행하는 Android 기기의 성능에 따라 값이 변경될 수 있습니다. 모범적인 방어적 프로그래밍이라는 차원에서 스트림의 구성을 확인한 후 사용하는 것이 좋습니다. 각 빌더 설정에 상응하는 스트림 설정을 가져오는 함수가 있습니다.

    AAudioStreamBuilder_setDeviceId() AAudioStream_getDeviceId()
    AAudioStreamBuilder_setDirection() AAudioStream_getDirection()
    AAudioStreamBuilder_setSharingMode() AAudioStream_getSharingMode()
    AAudioStreamBuilder_setSampleRate() AAudioStream_getSampleRate()
    AAudioStreamBuilder_setChannelCount() AAudioStream_getChannelCount()
    AAudioStreamBuilder_setFormat() AAudioStream_getFormat()
    AAudioStreamBuilder_setBufferCapacityInFrames() AAudioStream_getBufferCapacityInFrames()

  5. 빌더는 저장했다가 나중에 더 많은 스트림을 만들 때 재사용해도 됩니다. 다만 더 사용할 계획이 없다면 삭제하는 것이 좋습니다.

    AAudioStreamBuilder_delete(builder);
    

오디오 스트림 사용

상태 전환

AAudio 스트림은 보통 다섯 가지의 안정 상태(오류 상태인 연결 해제됨은 이 섹션의 마지막에 설명됨) 중 하나입니다.

  • 열림
  • 시작됨
  • 일시중지됨
  • 플러시됨
  • 중지됨

데이터는 스트림이 시작됨 상태일 때만 스트림을 통해 전달됩니다. 스트림의 상태를 변경하려면 상태 전환을 요청하는 다음 함수 중 하나를 사용하세요.

aaudio_result_t result;
result = AAudioStream_requestStart(stream);
result = AAudioStream_requestStop(stream);
result = AAudioStream_requestPause(stream);
result = AAudioStream_requestFlush(stream);

참고로 출력 스트림에는 일시중지 또는 플러시만 요청할 수 있습니다.

이러한 함수는 비동기이며 상태 변경이 즉시 발생하지 않습니다. 상태 변경을 요청할 때 스트림은 각각 상응하는 일시적인 상태 중 하나로 이동합니다.

  • 시작:
  • 일시중지 중
  • 플러시 중
  • 중지 중
  • 종료 중

아래의 상태 다이어그램에서는 안정 상태를 모서리가 둥근 직사각형, 일시적 상태를 점선 직사각형으로 표시했습니다. 그림에 표시되어 있지는 않지만 close()는 모든 상태에서 호출할 수 있습니다.

AAudio 수명 주기

AAudio는 상태 변화를 알리는 콜백을 제공하지 않습니다. 특수 함수인 AAudioStream_waitForStateChange(stream, inputState, nextState, timeout)를 사용하여 상태 변경을 기다릴 수 있습니다.

이 함수는 자체적으로 상태 변경을 감지하지 않으며 특정 상태를 기다리지 않습니다. 다만 현재 상태가 inputState다를 때를 기다리며 이는 사용자가 지정합니다.

예를 들어 일시중지를 요청한 뒤에는 스트림이 즉시 일시중지 중이라는 일시적 상태에 진입하고 어느 정도 시간이 흐른 뒤 일시중지됨 상태에 도달합니다(꼭 그렇게 된다는 보장은 없음). 일시중지됨 상태를 기다릴 수 없으므로 waitForStateChange()를 사용하여 일시중지 중 외의 모든 상태를 기다립니다. 방법은 다음과 같습니다.

aaudio_stream_state_t inputState = AAUDIO_STREAM_STATE_PAUSING;
aaudio_stream_state_t nextState = AAUDIO_STREAM_STATE_UNINITIALIZED;
int64_t timeoutNanos = 100 * AAUDIO_NANOS_PER_MILLISECOND;
result = AAudioStream_requestPause(stream);
result = AAudioStream_waitForStateChange(stream, inputState, &nextState, timeoutNanos);

스트림의 상태가 일시중지 중(호출 시 inputState가 현재 상태라고 가정)이 아니면 이 함수가 즉시 반환됩니다. 그러지 않으면 함수는 더 이상 일시중지 중 상태가 아니거나 시간 제한이 만료될 때까지 차단됩니다. 함수가 반환되면 매개변수 nextState가 스트림의 현재 상태를 나타냅니다.

요청 시작, 중지, 플러시를 호출한 후 상응하는 일시적 상태를 inputState로 이용하여 동일한 기법을 사용할 수 있습니다. 스트림이 종료되자마자 삭제되므로 AAudioStream_close()를 호출한 후 waitForStateChange()를 호출하면 안 됩니다. 또한 waitForStateChange()가 다른 스레드에서 실행 중일 때 AAudioStream_close()를 호출해서도 안 됩니다.

오디오 스트림 읽기 및 쓰기

스트림이 시작된 후 AAudioStream_read(stream, buffer, numFrames, timeoutNanos)AAudioStream_write(stream, buffer, numFrames, timeoutNanos) 함수를 사용하여 스트림을 읽거나 쓸 수 있습니다.

특정 프레임 수를 전송하는 블로킹 읽기 또는 블로킹 쓰기의 경우, timeoutNanos를 0보다 크게 설정합니다. 비블로킹 호출의 경우, timeoutNanos를 0으로 설정합니다. 이 경우 결과는 전송된 실제 프레임 수입니다.

입력을 읽을 때 올바른 프레임 수가 읽혔는지 확인해야 합니다. 확인하지 않으면 버퍼에 알 수 없는 데이터가 포함되어 오디오 결함을 초래할 수 있습니다. 0으로 버퍼를 보완하여 자동 드롭아웃을 생성할 수 있습니다.

aaudio_result_t result =
    AAudioStream_read(stream, audioData, numFrames, timeout);
if (result < 0) {
  // Error!
}
if (result != numFrames) {
  // pad the buffer with zeros
  memset(static_cast<sample_type*>(audioData) + result * samplesPerFrame, 0,
      sizeof(sample_type) * (numFrames - result) * samplesPerFrame);
}

스트림을 시작하기 전에 여기에 데이터 또는 무음을 작성하여 스트림 버퍼에 대비할 수 있습니다. 이 작업은 timeoutNanos가 0으로 설정된 비블로킹 호출에서 이루어져야 합니다.

버퍼 내의 데이터는 반드시 AAudioStream_getDataFormat()가 반환한 데이터 형식과 일치해야 합니다.

오디오 스트림 종료

스트림 사용이 완료되면 스트림을 종료합니다.

AAudioStream_close(stream);

스트림을 종료하고 나면 AAudio 스트림 기반 함수로 사용할 수 없습니다.

오디오 스트림 연결 끊김

다음 이벤트 중 하나가 발생하면 언제라도 오디오 스트림의 연결이 끊길 수 있습니다.

  • 관련 오디오 기기가 더 이상 연결되어 있지 않음(예: 헤드폰의 연결을 해제했을 때)
  • 내부에서 오류가 발생함
  • 오디오 기기가 더 이상 주 오디오 기기가 아님

스트림의 연결이 끊기면 '연결 끊김' 상태가 되며 write() 또는 AAUDIO_ERROR_DISCONNECTED를 반환하는 기타 함수 실행을 시도합니다. 스트림의 연결이 끊겼을 때 가능한 작업은 스트림을 종료하는 것뿐입니다.

오디오 기기의 연결이 끊겼을 때 알림을 받고 싶다면 AAudioStream_errorCallback 함수를 작성하고 AAudioStreamBuilder_setErrorCallback()을 사용하여 등록하세요.

이 콜백은 다음 예에 표시된 것처럼 스트림의 상태를 확인합니다. 스트림을 콜백에서 종료하거나 다시 열지 말고 대신 다른 스레드를 사용해야 합니다. 새롭게 연 스트림은 원래 스트림(예: framesPerBurst)과 다른 특성을 가질 수 있습니다.

void errorCallback(AAudioStream *stream,
                   void *userData,
                   aaudio_result_t error){

  aaudio_stream_state_t streamState = AAudioStream_getState(stream);
  if (streamState == AAUDIO_STREAM_STATE_DISCONNECTED){
    // Handle stream disconnect on a separate thread
    ...
  }
}

성능 최적화

오디오 애플리케이션의 내부 버퍼를 조정하고 특수한 높은 우선순위 스레드를 이용하여 성능을 최적화할 수 있습니다.

버퍼 조정을 통한 지연 시간 최소화

AAudio는 유지관리 대상인 내부 버퍼 안팎으로 데이터를 전달합니다. 내부 버퍼는 각 오디오 기기당 하나입니다.

버퍼의 용량은 버퍼가 유지할 수 있는 데이터의 총량입니다. AAudioStreamBuilder_setBufferCapacityInFrames()를 호출하여 용량을 설정할 수 있습니다. 이 방법은 기기에서 허용하는 최댓값에 할당할 수 있는 용량을 제한합니다. AAudioStream_getBufferCapacityInFrames()를 사용하여 버퍼의 실제 용량을 확인하세요.

앱은 버퍼의 전체 용량을 사용하지 않아도 됩니다. AAudio는 일정한 크기까지 버퍼를 채우는데, 이 크기는 사용자가 설정할 수 있습니다. 버퍼의 크기는 그 용량보다 크면 안 되고, 그보다 작은 경우가 많습니다. 버퍼 크기를 조절하면 이를 채우는 데 필요한 버스트의 수를 결정할 수 있고, 나아가 지연 시간을 제어할 수 있습니다. AAudioStreamBuilder_setBufferSizeInFrames()AAudioStreamBuilder_getBufferSizeInFrames() 메서드를 사용하여 버퍼 크기를 다룹니다.

애플리케이션은 오디오를 재생할 때 버퍼에 쓰기 작업을 하며 쓰기가 완료될 때까지 차단합니다. AAudio는 다른 형태의 버스트 버퍼에서 읽습니다. 각각의 버스트에는 여러 개의 오디오 프레임이 포함되며 대체로 읽히고 있는 버퍼보다 크기가 작습니다. 버스트 크기와 속도는 시스템이 제어하는데, 이 속성은 대개 오디오 기기의 회로에 따라 다릅니다. 사용자가 버스트의 크기나 버스트 속도를 변경할 수는 없지만, 이에 포함된 버스트의 수에 따라 내부 버퍼의 크기를 설정할 수는 있습니다. 일반적으로, 가장 낮은 지연 시간을 얻으려면 AAudioStream의 버퍼 크기를 보고된 버스트 크기의 배수로 설정하면 됩니다.

      AAudio 버퍼링

버퍼 크기를 최적화하는 방법 중 하나로 일단 큰 버퍼에서 시작하여 점차 크기를 줄여서 언더런이 시작되도록 한 다음 다시 조금씩 이동하여 크기를 늘리는 것이 있습니다. 아니면 작은 크기의 버퍼로 시작한 다음 여기서 언더런이 발생하면 버퍼 크기를 출력이 다시 깨끗하게 흐를 때까지 늘리는 것도 방법입니다.

이 프로세스는 사용자가 첫 사운드를 재생하기도 전에 매우 빠르게 일어날 수 있습니다. 첫 버퍼 크기 조정에는 무음을 사용해야 할 필요가 있을 수 있습니다. 이렇게 하면 사용자가 오디오 결함을 듣지 않도록 방지할 수 있기 때문입니다. 시스템 성능은 시간이 지남에 따라 변화할 수 있습니다(예: 사용자가 비행기 모드를 끌 수 있음). 버퍼 조정은 매우 미세한 오버헤드를 추가하기 때문에 앱이 데이터를 스트림에 읽거나 쓰는 동안 계속 진행할 수 있습니다.

다음은 버퍼 최적화 루프의 예시입니다.

int32_t previousUnderrunCount = 0;
int32_t framesPerBurst = AAudioStream_getFramesPerBurst(stream);
int32_t bufferSize = AAudioStream_getBufferSizeInFrames(stream);

int32_t bufferCapacity = AAudioStream_getBufferCapacityInFrames(stream);

while (go) {
    result = writeSomeData();
    if (result < 0) break;

    // Are we getting underruns?
    if (bufferSize < bufferCapacity) {
        int32_t underrunCount = AAudioStream_getXRunCount(stream);
        if (underrunCount > previousUnderrunCount) {
            previousUnderrunCount = underrunCount;
            // Try increasing the buffer size by one burst
            bufferSize += framesPerBurst;
            bufferSize = AAudioStream_setBufferSize(stream, bufferSize);
        }
    }
}

이 기법을 사용해서 입력 스트림에 대해 버퍼 크기를 최적화해도 큰 장점은 없습니다. 입력 스트림은 가능한 한 빨리 실행되어 버퍼링된 데이터양을 최소한의 수준으로 유지한 후 앱이 선점되면 이를 채우려 하기 때문입니다.

높은 우선순위 콜백 사용

일반적인 스레드에서 오디오 데이터를 읽거나 쓰는 앱은 선점되거나 타이밍 잡음을 경험할 수 있습니다. 이 때문에 오디오 결함이 발생할 수 있습니다. 큰 버퍼를 사용하면 그러한 결함을 방지할 수 있지만, 큰 버퍼는 오디오 지연 시간을 늘리기도 합니다. 짧은 지연 시간이 필요한 애플리케이션의 경우 오디오 스트림이 비동기 콜백 함수를 사용하여 데이터를 앱으로 전송하거나 앱에서 가져올 수 있습니다. AAudio는 더 나은 성능을 갖춘 더 높은 우선순위 스레드의 콜백을 실행합니다.

이 콜백 함수에는 다음과 같은 프로토타입이 있습니다.

typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames);

이 스트림 빌드를 사용하여 콜백을 등록합니다.

AAudioStreamBuilder_setDataCallback(builder, myCallback, myUserData);

가장 간단한 경우 이 스트림은 다음 버스트의 데이터를 획득하기 위해 주기적으로 콜백 함수를 실행합니다.

이 콜백 함수는 자신을 호출한 스트림에 읽기나 쓰기 작업을 할 수 없습니다. 콜백이 입력 스트림에 포함되면 코드는 audioData 버퍼에 공급된 데이터를 처리해야 합니다(3번째 인수로 지정됨). 콜백이 출력 스트림에 포함되면 코드는 데이터를 버퍼 내에 배치해야 합니다.

예를 들어 콜백을 사용하여 다음과 같이 사인파 출력을 계속해서 생성할 수 있습니다.

aaudio_data_callback_result_t myCallback(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames) {
    int64_t timeout = 0;

    // Write samples directly into the audioData array.
    generateSineWave(static_cast<float *>(audioData), numFrames);
    return AAUDIO_CALLABCK_RESULT_CONTINUE;
}

AAudio를 사용하여 두 개 이상의 스트림을 처리할 수 있습니다. 하나의 스트림을 마스터로 사용하고 포인터를 사용자 데이터 내 다른 스트림으로 전달할 수 있습니다. 콜백을 마스터 스트림에 등록합니다. 그런 다음 다른 스트림에 비블로킹 I/O를 사용합니다. 다음은 입력 스트림에서 출력 스트림으로 전달하는 왕복 콜백의 예시입니다. 마스터 호출 스트림은 출력 스트림입니다. 입력 스트림은 사용자 데이터 내에 포함되어 있습니다.

이 콜백은 출력 스트림의 버퍼에 데이터를 배치하는 입력 스트림 데이터에서 비블로킹 읽기 작업을 합니다.

aaudio_data_callback_result_t myCallback(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames) {
    AAudioStream *inputStream = (AAudioStream *) userData;
    int64_t timeout = 0;
    aaudio_result_t result =
        AAudioStream_read(inputStream, audioData, numFrames, timeout);

  if (result == numFrames)
      return AAUDIO_CALLABCK_RESULT_CONTINUE;
  if (result >= 0) {
      memset(static_cast<sample_type*>(audioData) + result * samplesPerFrame, 0,
          sizeof(sample_type) * (numFrames - result) * samplesPerFrame);
      return AAUDIO_CALLBACK_RESULT_CONTINUE;
  }
  return AAUDIO_CALLBACK_RESULT_STOP;
}

참고로 이 예시에서는 입력과 출력 스트림의 채널, 형식과 샘플링 레이트 수치가 같다고 가정하였습니다. 스트림의 형식은 코드가 변환을 올바르게 처리하는 한 서로 일치하지 않을 수 있습니다.

성능 모드 설정

모든 AAudioStream에는 앱의 동작에 큰 영향을 미치는 성능 모드가 있습니다. 모드는 모두 세 가지입니다.

  • AAUDIO_PERFORMANCE_MODE_NONE이 기본 모드입니다. 이 모드는 지연 시간과 절전의 균형을 이룬 기본 스트림을 사용합니다.
  • AAUDIO_PERFORMANCE_MODE_LOW_LATENCY는 작은 버퍼와 최적화된 데이터 경로를 사용하여 지연 시간을 단축합니다.
  • AAUDIO_PERFORMANCE_MODE_POWER_SAVING은 큰 내부 버퍼와 데이터 경로를 사용하여 지연 시간 면에서 타협하는 대신 전력을 절감합니다.

setPerformanceMode()를 호출하여 성능 모드를 선택할 수 있으며 현재 모드는 getPerformanceMode()를 호출하여 확인할 수 있습니다.

애플리케이션에 짧은 지연 시간이 절전보다 더 중요하다면 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY를 사용하세요. 이 모드는 게임이나 키보드 신시사이저와 같이 상호작용이 많은 앱에 유용합니다.

애플리케이션에 절전보다 짧은 지연 시간이 중요하다면 AAUDIO_PERFORMANCE_MODE_POWER_SAVING을 사용하세요. 이 모드는 오디오 또는 MIDI 파일 플레이어 스트리밍과 같이 이전에 생성한 음악을 재생하는 앱에 일반적입니다.

현재 AAudio 버전에서 가능한 한 짧은 지연 시간을 달성하려면 높은 우선순위 콜백과 함께 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 성능 모드를 사용해야 합니다. 다음 예를 따라 해 보세요.

// Create a stream builder
AAudioStreamBuilder *streamBuilder;
AAudio_createStreamBuilder(&streamBuilder);
AAudioStreamBuilder_setDataCallback(streamBuilder, dataCallback, nullptr);
AAudioStreamBuilder_setPerformanceMode(streamBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);

// Use it to create the stream
AAudioStream *stream;
AAudioStreamBuilder_openStream(streamBuilder, &stream);

스레드로부터 안전

AAudio API는 완전히 스레드로부터 안전하지 않습니다. 즉 일부 AAudio 함수는 한 번에 두 개 이상의 스레드에서 동시에 호출할 수 없습니다. 이는 AAudio가 스레드 선점이나 결함을 유발할 수 있는 뮤텍스 사용을 피하기 때문입니다.

만약을 위해, AAudioStream_waitForStateChange()를 호출하지 않거나 두 가지 다른 스레드에서 동일한 스트림에 읽기 또는 쓰기 작업을 하지 말아야 합니다. 마찬가지로 한 스레드에서 읽기 또는 쓰기 작업을 하는 중에 다른 스레드의 스트림을 종료하지 말아야 합니다.

AAudioStream_getSampleRate()AAudioStream_getChannelCount()와 같은 스트림 설정을 반환하는 호출은 스레드로부터 안전합니다.

다음과 같은 호출도 스레드로부터 안전합니다.

  • AAudio_convert*ToText()
  • AAudio_createStreamBuilder()
  • AAudioStream_get*()(AAudioStream_getTimestamp() 제외)

알려진 문제

  • blocking write()에서는 Android O DP2 릴리스가 FAST 트랙을 사용하지 않기 때문에 오디오 지연 시간이 깁니다. 콜백을 사용하여 지연 시간을 줄이세요.

추가 리소스

자세히 알아보려면 다음 리소스를 활용해 주시기 바랍니다.

API 참조

Codelab

동영상