네이티브 오디오 플러그인 SDK(Native Audio Plugin SDK)
오디오 프로파일러(Audio Profiler)

오디오 스페이셜라이저 SDK(Audio Spatializer SDK)

개요

오디오 스페이셜라이저 SDK는 네이티브 오디오 플러그인 SDK의 확장자로, 오디오 소스에서 주위 공간으로 오디오가 전송되는 방법을 변경하기 위해 사용할 수 있습니다. 오디오 소스의 빌트인 패닝은 소스를 받아 AudioListener와 AudioSource 사이의 거리와 각도에 따라 왼쪽 및 오른쪽 귀에 주어지는 게인을 조절한다는 점에서 간단한 형태의 공간화로 생각할 수 있습니다. 이것은 수평면 위의 플레이어에게 소리의 방향에 대한 간단한 단서를 제공합니다.

배경

가상 및 증강 현실 시스템의 출현으로 공간화 메서드는 플레이어의 몰입에 점점 더 중요한 핵심 컴포넌트가 되고 있습니다. 사람의 귀와 뇌는 소스에서 전송되어 왼쪽 및 오른쪽 귀에서 각각 수신되는 소리의 미세한 딜레이까지 정확하게 인지할 수 있습니다. 나아가 사람은 높은 주파수의 밸런스를 무의식으로 해석하여 오브젝트가 앞, 뒤, 위, 아래 중 어디에 있든지 구분할 수 있습니다. 또한 사람은 각 귀에서 들리는 소리의 차이에 의해 오브젝트가 부분적으로 가려져 있는지 알거나 반사된 소리에 의해 위치하고 있는 방의 형태에 대해 추측할 수 있습니다. 다시 말해 소리는 일상 활동에 매우 중요하지만 단지 그 중요성을 알아채지 못하고 있을 뿐입니다.

사운드 오클루전은 계산 성능이라는 측면에서 매우 해결하기 어려운 문제입니다. 전역 조명에서는 광원의 움직임을 사실상 순간적이라고 생각할 수 있는 데 반해, 소리는 매우 느리게 움직입니다. 따라서 실내에서 소리가 실제 움직이는 방식을 계산적으로 산출해 내는 것은 사실상 불가능합니다. 같은 이유로, 여러 문제를 다양한 범위로 해결하는 여러 공간화 접근 방법이 있습니다.

일부 해결책은 HRTF 문제만 해결합니다. HRTF는 Head-Related Transfer Function(헤드 관련 전송 기술)의 약자로, 대략 그래픽스 분야의 스피리컬 하모닉스에 비유할 수 있습니다. 다시 말해 귓바퀴, 머리 자체 그리고 어깨에서 오는 지향적 필터링은 물론 귀 사이의 미세한 딜레이를 포함하면서 양쪽 귀에 적용되는 소리의 방향에 영향을 받는 필터링이라 할 수 있습니다. HRTF 필터링 추가는 이미 기존 패닝 솔루션(가장 일반적이고 유명한 예는 가상 이발소의 입체음향 녹음)보다 방향 감각을 크게 향상시킵니다. 하지만 다이렉트 HRTF는 오디오의 다이렉트 경로하고만 관련이 있고 오디오가 공간 안에서 전송되는 방법과는 관련이 없기 때문에 다소 제한적입니다.

오클루전은 벽에 소리를 간접적으로 반사시킬 수 있다는 점에서 이보다 한 단계 더 발전한 것입니다. 다시 그래픽스 분야에서 비슷한 개념을 빌자면, 오클루전은 소스와 청취자 위치가 함께 결과를 결정함은 물론 반사된 각 방향성 소리 파형이 서로 다른 HRTF로 각 귀에 자극을 주고 파형이 이동한 경로의 길이에 따라 딜레이가 서로 다르다는 면에서 스페큘러 반사에 비교할 수 있습니다.

마지막으로, 소리가 방 안으로 퍼지고 여러 벽에서 반사된 후 각각 오디오 소스에 따라 방향과 누적 딜레이가 다른 오버랩된 파형의 필드로 귀에 닿는다는 점에서 여러 면에서 전역 조명 솔루션의 발산 부분에 해당하는 실내 반사가 있습니다.

SDK 및 예제 구현

해결해야 할 문제가 아주 많기 때문에, 오디오 공간화 솔루션은 많고 다양합니다. 이런 솔루션을 Unity에서 지원하는 최선의 방법은 네이티브 오디오 플러그인 SDK를 확장한 개방형 인터페이스인 오디오 스페이셜라이저 SDK를 만드는 것으로 확인되었습니다. 이 SDK를 사용하면 Unity의 표준 패너를 더 우수한 패너로 바꿀 수 있고, 계산에 필요한 소스와 청취자에 대한 중요한 메타데이터에 액세스할 수 있습니다.

스페이셜라이저의 구현 예는 여기에서 확인할 수 있습니다. 이 예는 다이렉트 HRTF만 지원하고 실제로 사용하려면 최적화할 필요가 있도록 의도적으로 간단하게 작성되었습니다. 플러그인은 간단한 리버브와 함께 제공되어 오디오 데이터가 어떻게 스페이셜라이저 플러그인에서 리버브 플러그인으로 전송될 수 있는지만 나타냅니다. HRTF 필터링은 MIT 미디어 랩의 Bill Gardner가 더미 헤드에서 수행한 양쪽 귀별 임펄스 반응 레코딩 세트인 KEMAR 데이터 세트를 기반으로 합니다. 이 임펄스 반응은 고속 푸리에 변환(Fast Fourier Transform)을 통한 고속 컨볼루션을 사용하여 입력 신호와 컨볼루션 됩니다. 위치 메타데이터는 적합한 임펄스 반응 세트를 선택하는 데만 사용됩니다. 데이터 세트는 머리 아래 –40도에서 머리 위 90도까지의 앙각에 대한 순환적으로 배열된 임펄스 반응으로 구성되기 때문입니다.

초기화

Unity에서 공간화 효과와 믹서 효과의 가장 큰 차이점은 각 소스가 해당 소스에서 생성된 오디오만을 처리하는 자체 효과 인스턴스를 가지도록 하기 위해 스페이셜라이저가 오디오 데이터 스트림을 만드는 오디오 소스 디코더 바로 뒤에 위치한다는 점입니다. 이 방법은 믹서 그룹에 연결된 다양한 오디오 소스로부터 전송된 혼합된 오디오를 처리하는 오디오 믹서 플러그인과 다릅니다. 플러그인이 이렇게 작동할 수 있게 하려면 효과의 설명 비트 필드에 플래그를 설정해야 합니다:

definition.flags |= UnityAudioEffectDefinitionFlags_IsSpatializer;

초기화 시에 이 플래그를 설정하면 플러그인 스캔 단계에서 이것이 스페이셜라이저이므로 플러그인의 인스턴스가 생성되면 UnityAudioEffectState 구조의 spatializerdata 멤버를 위한 UnityAudioSpatializerData 구조를 할당할 것임을 Unity에 알립니다.

스페이셜라이저를 프로젝트에서 사용할 수 있으려면 오디오 프로젝트 설정에서 먼저 선택해야 합니다.

스페셜라이저 플러그인 셀렉터
스페셜라이저 플러그인 셀렉터

AudioSource에서 스페이셜라이저 체크박스를 통해 스페이셜라이저를 사용하도록 설정할 수 있습니다. 이 설정은 AudioSource.spatialize 프로퍼티를 통해 스크립트에서 제어할 수도 있습니다. 사운드가 많은 게임에서는 인접 사운드에 대해서만 스페이셜라이저를 활성화하고 먼 사운드에는 기존 패닝을 사용하는 것이 합리적일 수 있습니다.

AudioSource의 스페이셜라이저 체크박스
AudioSource의 스페이셜라이저 체크박스

스페이셜라이저 효과 메타데이터

혼합 사운드에 대해 믹서에서 실행되는 다른 효과와 달리, 스페이셜라이저는 AudioSource에서 오디오 데이터를 디코딩한 직후에 적용됩니다. 따라서 각 스페이셜라이저 인스턴스에는 주로 AudioSource에 대한 데이터와 연관된 자체적인 UnityAudioSpatializerData의 인스턴스가 있습니다.

struct UnityAudioSpatializerData
{
    float listenermatrix[16]; // Matrix that transforms sourcepos into the local space of the listener
    float sourcematrix[16];   // Transform matrix of audio source
    float spatialblend;       // Distance-controlled spatial blend
    float reverbzonemix;      // Reverb zone mix level parameter (and curve) on audio source
    float spread;             // Spread parameter of the audio source (0..360 degrees)
    float stereopan;          // Stereo panning parameter of the audio source (-1: fully left, 1: fully right)
                              // The spatializer plugin may override the distance attenuation in order to
                              // influence the voice prioritization (leave this callback as NULL to use the
                              // built-in audio source attenuation curve)
    UnityAudioEffect_DistanceAttenuationCallback distanceattenuationcallback;
};

구조체에는 청취자와 소스에 대한 전체 4x4 트랜스폼 매트릭스가 있습니다. 리스너 매트릭스는 이미 역행렬화 되어 있으므로 상대 방향 벡터를 얻기 위해 두 매트릭스를 쉽게 곱할 수 있습니다. 청취자 매트릭스는 항상 직교 행렬이므로, 역을 계산하는 데 많은 리소스가 소모되지 않습니다. 게다가 구조에는 오디오 소스의 프로퍼티에 대응하는 공간 블렌드, 리버브 존 믹스, 스프레드 및 스테레오 팬 필드가 있습니다. 스페이셜라이저는 이런 필드를 정확하게 구현하는 책임을 집니다. Unity의 오디오 시스템은 활성화 시에 원시 소스 사운드를 (소스가 모노이거나 멀티 채널인 경우에도 업 또는 다운 믹싱을 사용해)스테레오 신호로만 제공하기 때문입니다.

매트릭스 규칙

소스매트릭스 필드에는 AudioSource와 관련된 변환 매트릭스의 일반 복사본이 포함됩니다. 회전되지 않은 게임 오브젝트의 일반 AudioSource의 경우, 포지션이 요소 12, 13, 14에 인코딩된 이동 매트릭스일 뿐입니다. 리스너매트릭스 필드에는 AudioListener와 연결된 트랜스폼 매트릭스의 역이 있습니다. 따라서 다음과 같이 매우 편리하게 리스너에서 소스까지의 방향 벡터를 결정할 수 있습니다.

float dir_x = L[0] * S[12] + L[4] * S[13] + L[ 8] * S[14] + L[12];
float dir_y = L[1] * S[12] + L[5] * S[13] + L[ 9] * S[14] + L[13];
float dir_z = L[2] * S[12] + L[6] * S[13] + L[10] * S[14] + L[14];

여기서 L은 리스너매트릭스이고 S는 소스매트릭스입니다. 회전되지 않고 1의 단위 스케일링을 가지는(카메라 매트릭스는 스케일하면 안 됨) 리스너매트릭스가 있는 경우, (L[12], L[13], L[14])의 포지션은 사실 Unity 인스펙터에서 보이는 값의 음수 값입니다. 그 이유는 리스너 매트릭스가 카메라의 변환 매트릭스의 역이기 때문입니다. 만약 카메라도 회전된 경우라면, 매트릭스에 단순히 마이너스를 붙이는 것만으로 포지션을 읽을 수는 없고 회전의 효과를 먼저 취소해야 합니다. 다행히 여기에 설명된 대로 이런 변환-회전-스케일링 매트릭스를 쉽게 반전시킬 수 있으므로, 왼쪽 상단의 3x3 회전 매트릭스 L을 전치하고 다음과 같이 포지션을 계산해야 합니다.

float listenerpos_x = -(L[0] * L[12] + L[ 1] * L[13] + L[ 2] * L[14]);
float listenerpos_y = -(L[4] * L[12] + L[ 5] * L[13] + L[ 6] * L[14]);
float listenerpos_z = -(L[8] * L[12] + L[ 9] * L[13] + L[10] * L[14]);

감쇠 곡선과 및 가청도

여전히 Unity 오디오 시스템에 의해 유일하게 처리되는 것은 거리 감쇠로, 사운드의 공간화 단계 이전에 적용되며, 오디오 시스템이 사용자 정의 Max Real Voices한계치를 충족하도록 중요도에 기반한 사운드의 동적 가상화에 사용되는 소스의 대략적인 가청도를 알기 위해 필요합니다. 이것은 ‘닭이 먼저냐 달걀이 먼저냐’ 문제와 같아서 이 정보는 실제 신호 레벨 측정에서 얻어지지 않고 거리-조절 감쇠 커브, 볼륨 프로퍼티, 그리고 믹서에 의해 적용되는 감쇠에서 읽는 값의 조합에 해당합니다. 하지만 감쇠 곡선을 사용자 지정 감쇠 커브로 오버라이드하거나 AudioSource의 커브를 통해 계산된 값을 수정 기준으로 사용할 수도 있습니다. 이렇게 하기 위해 다음과 같이 구현할 수 있는 콜백이 UnityAudioSpatializerData 구조에 있습니다.

typedef UNITY_AUDIODSP_RESULT (UNITY_AUDIODSP_CALLBACK* UnityAudioEffect_DistanceAttenuationCallback)(
    UnityAudioEffectState* state,
    float distanceIn,
    float attenuationIn,
    float* attenuationOut);

간단한 커스텀 로그 커브를 다음과 같이 구현할 수 있습니다.

UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACK SimpleLogAttenuation(
    UnityAudioEffectState* state,
    float distanceIn,
    float attenuationIn,
    float* attenuationOut)
{
    const float rollOffScale = 1.0f; // Similar to the one in the Audio Project Settings
    *attenuationOut = 1.0f / max(1.0f, rollOffScale * distanceIn);
    return UNITY_AUDIODSP_OK;
}

스크립트 API

AudioSource에는 또한 스페이셜라이저 효과의 파라미터를 설정하고 가져오기 위해 사용할 수 있는 2개의 새로운 메서드가 있어 네이티브 측을 보완합니다. 메서드의 이름은 SetSpatializerFloat/GetSpatializerFloat로, 일반 네이티브 오디오 플러그인 인터페이스에서 사용되는 SetFloatParameter/GetFloatParameter와 비슷하게 작동합니다. 가장 중요한 차이점은 SetSpatializerFloat/GetSpatializerFloat가 설정하거나 읽을 파라미터를 사용하고 해당 파라미터에 인덱싱하는 반면 SetSpatializerFloat/GetSpatializerFloat는 이름을 사용하여 파라미터를 의미한다는 것입니다.

또한 AudioSource.spatializer 부울 프로퍼티는 AudioSource 인스펙터의 체크박스에 연결되고 오디오 프로젝트 세팅에서 선택하는 항목에 따라 스페이셜라이저 효과의 인스턴스화와 할당해제를 제어합니다. 스페이셜라이저 효과를 인스턴스화하는 데(메모리 할당, 사전 연산 등과 관련하여) 리소스가 너무 많이 소모되는 경우 Unity 플러그인 인터페이스 바인딩을 매우 가볍게 유지하고 활성화/비활성화로 인해 프레임 드롭이 발생하지 않토록 효과를 풀에서 동적으로 할당하는 방안을 고려해 볼 수 있습니다.

플러그인 예제의 알려진 한계

빠른 컨볼루션 알고리즘이 사용됨으로 인해 고속으로 이동하면 어느 정도의 지퍼 아티팩트가 발생합니다. 이 아티팩트는 오버랩 세이브 컨볼루션이나 크로스 페이딩 버퍼를 사용하여 제거할 수 있습니다. 또한 코드가 머리를 옆으로 기울이는 틸트를 지원하지 않지만, 이 문제는 쉽게 수정할 수 있습니다. 이 데모에는 KEMAR 데이터 세트만 사용됩니다. IRCAM에는 인간 피험자로부터 얻은 소수의 가용 데이터 세트가 있습니다.

네이티브 오디오 플러그인 SDK(Native Audio Plugin SDK)
오디오 프로파일러(Audio Profiler)