FMOD 통합 및 기본적인 Audio API 설계

2025. 3. 27. 19:36WIP/Igniter

 

 

가장 기본적인 API 설계로 'AudioSystem'과 'AudioSourceComponent' 두 가지 요소를 추가하였다. 이 두가지를 만드는데 있어서 고민했던 점이 어떤 시점에 FMOD API를 호출하도록 할 것인가였다.

예를 들어, 단순히 Entity-Component 모델의 관점에서 보았을때는 단순히 아래와 같은 형태를 상상 할 수 있다.

audioSource->Play();
audioSource->Stop();
status = audioSource->GetStatus();
...

하지만 ECS에서의 Component는 로직을 제외한 순수한 데이터만을 저장하는 형태를 띄기 때문에 위와 같이 직접적으로 로직을 포함한 함수를 구현하는 것은 적절지 못하다.

현재의 가장 초기 버전의 경우는 ECS의 원칙에 따라 컴포넌트에 순수하게 데이터만을 가지고 있으며. 매 프레임, 메인 로직 업데이트 단계가 끝난 이후 AudioSystem이 이들을 순회하여 적절한 행동을 하는 형태로 구현되어 있다.

이때, 일반적으로 AudioSystem에서 이 모든 값들을 일일히 추적하지 않는 이상 값의 변화를 알아차릴 수 없기 때문에(단순히 컴포넌트의 데이터만 변경하는 경우). 현재 프레임에서 값들을 FMOD에 반영해줘야 함을 별도로 플래그가 설정되어야만 값들을 반영해주도록 하였다.

Audio Source의 행동(재생, 재생 및 일시정지, 일시정지, 재개, 정지)도 NextEvent를 설정하여 AudioSystem에 의해 자동으로 처리 될 수 있도록 하였다.

FMOD API의 경우, 소리를 재생하면 유니크한 채널 핸들을 할당해준다. 이들은 FMOD::System에 의해서 자동으로 관리되고, 소리의 재생이 완료되거나 수동으로 정지(stop)해주면 그 수명이 만료되게 된다.

AudioSourceComponent에 이들을 포함시키는 방법도 있지만, FMOD::Channel 자체가 로직을 포함하고, 이에 자유롭게 접근 할 수 있다면 ECS 구조를 깰 뿐 아니라 컴포넌트에 설정된 값이 쓸모 없어지기 때문에, AudioSystem 내부에서만 사용 및 자동으로 관리 하도록 하고, 외부에는 노출하지 않도록 구현하였다.

이전 포스트 중, 렌더링 프로세스 전체를 태스크 기반으로 동시적으로(concurrently) 처리가 될 수 있도록 하였는데 거기에 더해 이번 Audio System의 업데이트 프로세스도 렌더링 프로세스와 동시적으로 처리 될 수 있도록 구성 하였다.

아직 까지는 오디오를 에셋화 시키진 않았고 단순히 구조와 작동 방식을 간단하게 시험해보았지만, 꽤 나쁘지 않아서 여기서 추가로 발전시킬 것 같다.

AudioClip: 오디오 에셋

Audio: FMOD::Sound 핸들을 미믹킹(실제 클래스가 아닌, 단순히 FMOD API를 직접적으로 노출하지 않기 위함)

AudioSourceComponent: 소리의 근원지를 나타내는 엔티티가 소유할 컴포넌트

TransformComponent와 AudioSourceComponent를 같이 소유하는 경우 3D 공간상에 배치되어 있다고 보고, 위치 정보나 3D 관련 셋팅을 업데이트 할 수 있도록 함 (Spatial Audio Source Archetype).

AudioListenerComponent: 해당 컴포넌트가 존재하는 경우에만 AudioSystem이 작동, 또한 이를 소유한 엔티티의 경우 TransformComponent의 소유 또한 요구됨(Spatial Audio Source를 듣는 객체의 위치 정보를 위해)

AudioSystem: Audio의 할당 및 해제를 담당, AudioSourceComponent 데이터를 기반으로 소리 재생에 필요한 연산 및 작업을 진행.

기본적인 오디오 API 작업은 이번엔 이정도 수준으로 마무리 시키고, 추후엔 Steam Audio의 통합도 고려하고자 한다.