버전

menu_open
Wwise SDK 2019.2.15
Low-Level I/O
Default Streaming Manager Information

Low-Level I/O는 상위 레벨 Stream Manager API 기본 구현의 하위 모듈로서, 상위 레벨 Stream Manager API보다 훨씬 더 간단히 구현되는 I/O 전송 인터페이스를 제공합니다. 따라서 Low-Level I/O는 오직 Stream Manager의 기본 구현만 관련이 있습니다.

소개

Low-Level I/O 시스템은 Audiokinetic의 Stream Manager 구현에 특화돼있습니다. 해당 인터페이스는 <Wwise Installation>/SDK/include/AK/SoundEngine/AkStreamMgrModule.h에 정의돼있습니다.

Low-Level I/O 시스템에는 두 가지 용도가 있습니다.

  • 파일 위치 결정
  • 실제 I/O 전송 추상화

AK::StreamMgr::IAkFileLocationResolver 를 구현하는 유일한 오브젝트는 File Location Resolver라고 불리며, Stream Manager에 등록돼야 합니다 (AK::StreamMgr::SetFileLocationResolver() 사용). Stream Manager는 표준 스트림이나 자동 스트림을 생성할 때마다 AK::StreamMgr::IAkFileLocationResolver::Open() 을 호출합니다. 이 메소드는 파일 크기 및 다른 정보와 함께 유효한 파일 설명자(AkFileDesc)를 반환해야 하며, 여기에는 이 파일을 스트림할 때 사용할 상위 레벨 스트리밍장치 ID가 있어야 합니다. 스트리밍 장치는 AK::StreamMgr::CreateDevice()를 이용해 Stream Manager에 생성되고 등록됩니다.

각 스트리밍 장치에 대해 하위 레벨 I/O 연결이 제공돼야 합니다. I/O 연결은 AK::StreamMgr::IAkIOHookBlockingAK::StreamMgr::IAkIOHookDeferredBatch , 둘 중 하나의 인터페이스를 구현합니다. 스트리밍 장치가 I/O 전송을 수행할 때에는 하위 레벨 I/O 연결의 AK::StreamMgr::IAkIOHookBlocking::Read(), AK::StreamMgr::IAkIOHookBlocking::Write(), AK::StreamMgr::IAkIOHookDeferredBatch::BatchRead(), 또는 AK::StreamMgr::IAkIOHookDeferredBatch::BatchWrite() 메소드를 호출합니다. 플랫폼의 I/O API의 직접 호출이 High-Level Stream Manager로부터 발생하는 일은 없습니다. File Location Resolver와 I/O 연결이 함께 Low-Level I/O 시스템을 구성합니다. 저장 장치 제약, 파일, 플랫폼 I/O별로 다른 모든 것들은 Low-Level I/O 시스템이 관리합니다.

다음 표는 Low-Level I/O 시스템 인터페이스와 이들이 Stream Manager에 어떻게 보이는지를 나타냅니다.

게임 타이틀은 파일 위치를 결정하고 실제 I/O 전송을 수행하기 위해 Low-Level I/O 인터페이스를 구현해야 합니다. Wwise 사운드 엔진의 I/O 관리를 자신의 게임에 통합하는 가장 쉽고 빠른 방법은, Stream Manager의 기본 설정 구현을 이용하고 Low-Level I/O 시스템을 구현하는 것입니다. 여기서부터 시작해 네이티브 파일 읽기를 수행하거나, I/O 요청을 자신의 I/O 관리자로 라우팅하면 됩니다.

Wwise SDK에는 Low-Level I/O의 기본 구현이 포함돼있습니다. 제공된느 그대로 사용하거나, 자신의 구현을 시작하는 밑그림으로 사용해도 됩니다. Low-Level I/O 예제와 관련해 더 자세한 개념 설명은
샘플 기본 구현 설명 섹션을 참고하세요.

파일 위치 결정

File Location Resolver

File Location Resolver는 AK::StreamMgr::SetFileLocationResolver() 를 이용해 Stream Manager에 등록돼야 합니다. Stream Manager가 스트림 오브젝트를 생성할 때마다 이 인스턴스의 Open() 메소드를 호출합니다. AK::StreamMgr::IAkFileLocationResolver::Open() 은 파일 설명자 구조체(AkFileDesc)에 내용을 입력해야 합니다. 같은 개수의 파일이 Stream Manager 에서 스트림 오브젝트로 Low-Level I/O에 동시에 존재하고 있습니다.

게임은 AK::StreamMgr::CreateDevice()를 이용해 Stream Manager에 최소 하나의 스트리밍 장치를 생성해야 합니다. 그러나 필요한 만큼 여러 개의 장치를 생성할 수 있습니다. 각 스트리밍 장치는 각자의 스레드에서 실행되며, I/O 요청을 각자의 I/O 연결로 전송합니다. 일반적으로 한 물리적 장치당 하나의 스트리밍 장치를 생성해야 합니다. AkFileDesc 구조체에 deviceID라는 영역이 있습니다. File Location Resolver는 생성된 스트리밍 장치 중 하나의 deviceID로 설정해야 합니다. 이는 파일 처리를 적절한 장치로 어떻게 전달하는지 나타냅니다. 또한, 파일의 크기와 오프셋(uSector)이 반환돼야 하며, 시스템 파일 핸들이 생성돼야 합니다 (필요 없는 경우에도).

파일 설명

Stream Manager의 클라이언트는 문자열(const AkOSChar *)이나 ID (AkFileID, 정수), 또는 파일 설명자를 이용해 파일을 식별합니다. 그렇기 때문에 Stream Manager의 스트림 생성 메소드(AK::IAkStreamMgr::CreateStd(), AK::IAkStreamMgr::CreateAuto())에는 두 개의 오버로드가 있습니다. File Location Resolver에는 파일 이름과 파일 ID를 파일 설명자로 매핑하는 과제가 있어, 두 개의 AK::StreamMgr::IAkFileLocationResolver::Open() 의 오버로드가 있습니다. 파일 설명자는 모든 하위 레벨 전송과 정보 쿼리의 데이터 원본을 식별하는 데 사용됩니다.

AK::StreamMgr::IAkFileLocationResolver::Open() 가 파일 설명자에 내용을 입력해야 합니다. 이 파일 설명자는 하위 레벨 I/O 연결의 메소드가 호출될 때마다 다시 전송됩니다. 해당 구조체의 세 가지 멤버는 High-Level Stream Manager가 사용합니다.

  • deviceID: AK::CreateDevice() 호출로 얻어진 유효한 장치 ID여야 합니다. 올바른 상위 레벨 장치에 파일을 연결하기 위해 Stream Manager가 사용합니다. 연결이 되면, 장치를 생성할 때 전달된 I/O 연결을 통해 I/O 전송이 실행됩니다.
  • uSector: 설명된 파일 시작의 오프셋 uSector는 AkFileHandle AkFileDesc::hFile 로 표시되는 파일의 시작과 관련돼있습니다. 블록 (부분)으로 나타내며, 이 파일 설명자에 대해 AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() 가 반환하는 값에 해당하는 크기를 말합니다.

스트리밍 장치가 I/O 전송 메소드를 호출할 때, AkTransferInfo 구조체의 일부로서 파일 시작에서 하위 레벨 I/O 연결로 오프셋(단위: 바이트)을 전송합니다. 오프셋은 다음과 같이 계산합니다. Current_Position + ( AkFileDesc::uSector * Block_Size) 블록 크기 또한 이 연결을 통해 파일 설명자당 한 번씩 쿼리된다는 사실에 유의하세요.

나머지 멤버는 Low-Level I/O 시스템이 독점으로 소유합니다. 예를 들어, AkFileHandle의 경우, Win32에서 HANDLE로 타입이 정의돼있어, Win32의 ReadFile()로 전송되는 실제 유효한 파일 핸들을 가질 수 있습니다. 그러나 AkFileHandle를 ID나 포인터로 사용할 수도 있습니다. 상위 레벨 장치는 파일 설명자 영역을 변경하거나 읽지 않습니다. 따라서 게임 디스크에 파일들이 과다하게 열려있을 경우 Low-Level I/O는 파일 핸들을 자유롭게 닫거나 다시 열 수 있습니다.

작은 정보: AK::StreamMgr::IAkFileLocationResolver::Open() 으로 전달된 in_fileDesc 파일 설명자 구조체의 주소는 연관된 스트림 오브젝트의 수명에 맞춰서 동일하게 유지됩니다.

지연 열기

AK::StreamMgr::IAkFileLocationResolver::Open() 은 또 다른 인자인 io_bSyncOpen을 받습니다. io_bSyncOpen 이 true이면, 해당 파일은 바로 열리고 그 설명자는 유효한 정보를 담고 있어야 한다는 뜻입니다. 그러나 io_bSyncOpen이 false이면 파일을 열기 전에 File Location Resolver가 나중으로 미룰 수 있습니다. 왜 미루는 것일까요? 클라이언트의 스레드에서 AK::IAkStreamMgr::CreateStd()AK::IAkStreamMgr::CreateAuto()AK::StreamMgr::IAkFileLocationResolver::Open() 을 호출하기 때문입니다. 파일을 여는데 시간이 오래 걸리면, I/O 스레드가 열게끔 미루는 편이 나을 수 있습니다. 예를 들어 사운드 엔진은 오디오 스레드에 스트림을 생성하고, 이는 Open()을 차단합니다. 시간이 오래 걸릴 경우, 오디오가 버벅거리는 소리가 들릴 수 있습니다 (보이스 고갈 상태).

io_bSyncOpen 이 false이면 파일을 곧바로 열 수도 있고 그렇지 않을 수도 있습니다. 만약 파일을 열 경우, AkFileDesc 구조체를 채워넣고 플래그를 true로 설정해 파일을 이미 열었음을 표시합니다. 열지 않을 경우, 아무런 처리도 필요하지 않지만, AkFileDesc의 deviceID 영역을 설정해줘야 합니다. 어느 장치의 스레드가 이 파일을 맡을 것인지 Stream Manager에게 알려주는 것이 매우 중요합니다. 파일이 장치로 발송되면 변경할 수 없습니다. 그 이후 발생한 deviceID 수정 내용은 무시됩니다.

경고: 지금 바로 열기를 요청한 파일의 열기는 지연시킬 수 없습니다. 즉, io_bSyncOpen을 재설정할 수 없습니다.

열기를 지연시키면, AkFileDesc::deviceID로 지정된 스트리밍 장치가 이 스트림 오브젝트의 소유권을 갖게 됩니다. I/O 연결의 Read() 호출 전에 해당 스레드로부터 AK::StreamMgr::IAkFileLocationResolver::Open() 을 다시 한 번 호출합니다. 다만 이번에는 io_bSyncOpentrue로 설정합니다.

경고: 파일 열기를 지연시키면 비용이 늘어납니다. 파일 열기 데이터(플래그 및 파일 이름)는 AK::StreamMgr::IAkFileLocationResolver::Open() 가 다시 호출되기까지 저장해놓아야 합니다. 필요한 경우를 제외하고는 파일 열기를 지연시키지 않는 것이 좋습니다.

파일 시스템 플래그

Stream Manager의 AK::IAkStreamMgr::CreateStd()AK::IAkStreamMgr::CreateAuto() 메소드는 AkFileSystemFlags 구조체를 가리키는 포인터를 받으며, 이는 AK::StreamMgr::IAkFileLocationResolver::Open()로 전달됩니다. 이 구조체는 사용자에서 Low-Level I/O로 직접 정보를 전달하는 방법입니다. 이 정보는 파일 위치 논리같은 것을 완료하는 데 사용됩니다. Stream Manager의 일반 사용자는 NULL을 전달할 수 있지만, 사운드 엔진은 항상 관련 정보로 채워진 구조체를 전달해 File Location Resolver에게 사운드 엔진에서 온 요청임을 알립니다.

파일 시스템 플래그 구조체에는 다음과 같은 입력란이 있습니다.

  • uCompanyID: 사운드 엔진은 항상 이 입력란을 AKCOMPANYID_AUDIOKINETIC으로 설정해, Low-Level I/O를 구현하는 사용자에게 해당 파일은 사운드 엔진으로 읽어야 한다는 것을 알립니다. 스트리밍된 외부 음원의 경우 사운드 엔진은 AKCOMPANYID_AUDIOKINETIC_EXTERNAL 을 전달합니다.
  • uCodecID: 이 입력란은 파일 타입을 구분하는 데 사용할 수 있습니다. 파일 타입은 사운드 엔진에 의해 사용돼야 합니다. 사운드 엔진이 사용하는 코덱 ID는 AkTypes.h에 정의돼있습니다. 호스트 프로그램 또한 companyID를 AKCOMPANYID_AUDIOKINETIC 이나 AKCOMPANYID_AUDIOKINETIC_EXTERNAL 로설정하지 않는 한, 해당 ID를 동일한 값으로 정의할 수 있습니다.
  • bIsLanguageSpecific: 이 입력란은 검색 중인 파일이 현재 언어에 한정돼있는지를 표시합니다. 일반적으로 특정 언어에 한정된 콘텐츠를 담고 있는 파일은 서로 다른 위치에 있습니다. Low-Level I/O는 현재 선택된 언어에 따라 경로를 결정해야 합니다. 더 자세한 정보는 언어-전용 ("Voice"와 "Mixed") SoundBank 를 참고하세요.
  • 커스텀 매개 변수와 크기: 파일 위치 스킴에서 특정 게임에 한정된 확장자에 사용됩니다. 예를 들어 파일 설명자에 확장자를 복사할 수 있습니다. 사운드 엔진은 언제나 0을 전달합니다.

사운드 엔진 파일의 해결

사운드 엔진은 SoundBank 파일과 스트림된 오디오 파일을 읽습니다. 이번 하위 섹션에서는 Low-Level I/O가 해당 식별자를 실제 파일로 어떻게 해결하는지 설명합니다.

AK::StreamMgr::IAkFileLocationResolver::Open() 에서 받은 ID를 Low-Level I/O의 유효한 파일 설명자로 매핑하는 데에는 다양한 방법이 있습니다.

  • 파일 이름에 ID 매핑을 구현해 사용하기
  • ID를 이용해 파일 이름 문자열 생성하기
  • 모든 스트리밍된 오디오 파일을 연결하는 큰 파일의 시스템 핸들을 가져와 (예: File Packager 애플리케이션으로 생성된 파일 패키지 - Wwise 도움말 참고), 이 큰 파일의 크기와 오프셋을 정의하는 파일 설명자 구조체로 ID들의 맵을 구현하고 사용.

Low-Level I/O의 SDK 예제는 서로 다른 두 개의 방식을 사용해 파일 위치를 결정합니다. 하나는 CAkFileLocationBase로서, 전역적으로 된 경로를 연결시켜, 플랫폼 fopen() 메소드로 사용할 수 있는 전체 경로 문자열을 생성하는 방법입니다. 또 다른 하나는 CAkFilePackageLowLevelIO로, File Packager 유틸리티로 생성된 파일 패키지를 관리합니다. 파일 패키지는 단순히 많은 파일들이 연결된 하나의 큰 파일로서, 각 원본 파일의 관련 오프셋을 표시하는 헤더가 있습니다.

File Location Resolver의 구현은 SDK 예제에 나와있습니다. 이 예제에서는 다양한 방식을 이용해 파일 위치를 관리합니다. 이 방식들은 이 섹션의 마지막 예제 코드 설명에 나와있습니다.

Low-Level I/O의 기본 구현에 사용하는 방식에 대한 자세한 설명은 기본 파일 위치 를 참고하세요.

File Packages(CAkFilePackageLowLevelIO)를 사용하는 Low-Level I/O 구현에 사용하는 방식에 대한 자세한 설명은 파일 위치 을 참고하세요.

SoundBank

주요 API( AK::SoundEngine::LoadBank()AK::SoundEngine::PrepareEvent())로부터 뱅크를 로드하는 명시적 요청이나 암묵적 요청에 따라, 사운드 엔진의 Bank Manager가 ANSI 문자열이나 AK::IAkStreamMgr::CreateStd() 의 ID 오버로드를 호출할 수 있습니다. 어떤 오버로드가 선택되는지 결정하는 각기 다른 환경에 대해서는 파일 시스템에서 뱅크의 로드 를 참고하세요.

두 경우 모두 파일 시스템 플래그에서 AKCOMPANYID_AUDIOKINETIC 을 Company ID로 사용하며, AKCODECID_BANK 를 코덱 ID로 사용합니다.

사운드 엔진 API의 LoadBank() 메소드는 해당 뱅크가 특정 언어에 한정돼있는지를 명시하는 플래그를 표시하지 않습니다. 이는 Low-Level I/O 구현이 결정할 일입니다. 사운드 엔진은 SoundBank가 특정 언어에 한정돼있는지 알지 못하므로 bIsLanguageSpecific 플래그를 True로 하여 Stream Manager를 호출합니다. Stream Manager(Low-Level I/O)가 뱅크를 여는 데 실패하면 사운드 엔진이 이번에는 bIsLanguageSpecific 플래그를 False로 설정해 다시 시도합니다. Wwise SDK에서 특정 언어에 한정된 뱅크를 다루는 방법에 대한 자세한 정보는 언어-전용 ("Voice"와 "Mixed") SoundBank 를 참고하세요.

작은 정보: Bank Manager가 현지화되지 않은 뱅크에 대해 Stream Manager를 두 번 호출하는 것을 피하려면 bIsLanguageSpecific 플래그를 무시하고 올바른 위치에서 해당 사운드뱅크를 직접 열어야 합니다. 작업하는 사람만이 어느 사운드뱅크가 어디 있는지 알고 있기 때문입니다. 또한, AK::SoundEngine::LoadBank() 의 비동기 버전으로 전달하는 쿠키는 in_pFlags->pCustomData의 값으로 AK::StreamMgr::IAkFileLocationResolver::Open() 에 전달됩니다. 해당 뱅크를 특정 언어에 특화된 디렉터리로부터 열어야할 지 결정하는 데 이 쿠키를 사용할 수 있습니다.

스트리밍된 오디오 파일

스트리밍된 파일 레퍼런스는 정수 ID로 뱅크에 저장됩니다. 스트리밍될 변환된 오디오 파일의 실제 파일 경로는 SoundBanksInfo.xml 파일에 들어있으며, 해당 뱅크와 함께 생성됩니다 (보다 자세한 정보는 SoundBanksInfo.xml 참고).

작은 정보: Wwise SoundBank 설정에서 'Copy streamed files'를 선택하면 [ID].[ext] 식으로 이름을 변경해 특정 플랫폼의 Generated SoundBanks 폴더에 자동으로 복사합니다. 더 많은 정보는 스트림된 오디오 파일 를 참고하세요. 기본 파일 위치 구현(CAkFileLocationBase)은 'Copy streamed files' 옵션에 사용하도록 고안되었습니다.

사운드 엔진은 스트리밍된 오디오 파일을 재생하고자 할 때 AK::IAkStreamMgr::CreateAuto() 의 ID 오버로드를 호출합니다. 그러면 AK::StreamMgr::IAkFileLocationResolver::Open() 의 ID 오버로드로 전달됩니다. ID와 함께 AkFileSystemFlags 구조체를 다음 정보를 이용해 전달합니다.

  • uCompanyID는 AKCOMPANYID_AUDIOKINETIC입니다.
  • uCodecID는 AkTypes.h에 정의된 오디오 형식 중 하나입니다 (AKCODECID_XXX).
  • 현재 게임 언어에 따른 위치에서 파일을 검색해야할 때는 true이고, 그렇지 않은 경우에는 false인 bIsLanguageSpecific 플래그.

I/O 전송 인터페이스

파일 위치가 결정되면 Stream Manager가 파일 설명자를 적절한 스트리밍 장치로 전달하고, 이는 Low-Level I/O 시스템과 I/O 연결을 통해 상호작용합니다. 그러면 가장 먼저 AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() 를 호출해 하위 레벨 블록 크기 제약을 쿼리합니다. 그런 다음, I/O 연결의 Read() 메소드를 통해 모든 입력 데이터 전송이 실행되며, I/O 연결의 Write() 메소드로 출력됩니다. 스트림이 삭제될 때는 AK::StreamMgr::IAkLowLevelIOHook::Close() 가 호출됩니다.

각각의 메소드는 File Location Resolver에 의해 채워진 동일한 파일 설명자를 전달합니다.

상위 레벨 장치 지정

현재 Stream Manager 구현은 두 유형의 스트리밍 장치를 정의합니다. 따라서 I/O 연결도 두 가지 종류가 있습니다. 하나는 Low-Level I/O(AK::StreamMgr::IAkIOHookBlocking)로 동기식 핸드셰이킹을 사용하고, 다른 하나는 비동기식 핸드셰이킹(AK::StreamMgr::IAkIOHookDeferredBatch)을 사용합니다. 이 두 인터페이스는 AK::StreamMgr::IAkLowLevelIOHook로부터 상속됩니다.

AK::StreamMgr::CreateDevice() 는 장치 전반에 대한 초기화 설정이 들어있는 AkDeviceSettings 라는 구조체를 전달합니다. 그 중 하나인 AkDeviceSettings::uSchedulerTypeFlags 는 상위 레벨 I/O 스케줄러의 타입을 정의합니다. AK::StreamMgr::IAkIOHookBlocking 과 작동하는 차단 장치를 생성하고자 할 경우에는 이 타입을 AK_SCHEDULER_BLOCKING으로 설정하고, AK::StreamMgr::IAkIOHookDeferredBatch 와 작동하는 지연 장치를 생성할 경우에는 AK_SCHEDULER_DEFERRED_LINED_UP을 설정합니다. 적합한 하위 레벨 I/O 연결의 인스턴스가 AK::StreamMgr::CreateDevice() 로 전달돼야 합니다.

경고: AK::StreamMgr::IAkIOHookDeferredBatch 의 인스턴스를 AK_SCHEDULER_BLOCKING 장치로 전달하거나, 혹은 그 반대는 허용되지 않습니다. 이는 결과적으로 생성될 때 조용히 실패하고 런타임에서 강제로 종료됩니다.

AK::StreamMgr::CreateDevice() 는 File Location Resolver에 의해 파일 설명자 구조체에 설정된 장치 ID를 반환합니다.

다음 두 섹션에서는 차단 I/O 연결과 지연 I/O 연결에 대해 설명합니다.

차단 I/O 연결

AK_SCHEDULER_BLOCKING 플래그가 지정되면 Stream Manager가 AK::StreamMgr::IAkIOHookBlocking 인터페이스를 통해 Low-Level I/O 시스템과 상호작용하는 스트리밍 장치를 생성합니다.

차단 인터페이스는 지연 인터페이스보다 간단합니다. 차단 인터페이스는 AK::StreamMgr::IAkIOHookBlocking::Read()AK::StreamMgr::IAkIOHookBlocking::Write(), 이 두 메소드를 정의하며, 이들은 I/O 전송이 완료되었을 때에만 반환합니다.

경고: Low-Level I/O 단의 동기식 I/O 전송을 Stream Manager 단의 (또는 AK::SoundEngine::LoadBank() 에서 사운드 엔진 단의) 동기식 스트림 접근과 혼동하지 마세요. 이들은 서로 전혀 관련이 없습니다. 스트리밍 장치는 그 자체의 스레드에서 실행된다는 점을 기억하세요. 이 스레드는 상위 레벨 IAkStdStream 및 IAkAutoStream 접근과 하위 레벨 I/O 전송을 분리시킵니다. 따라서 차단 하위 레벨 I/O 연결은 Stream Manager의 비차단 스트림 접근뿐만 아니라 지연 스트림 접근 및 반대의 경우를 다루기도 합니다.

AK_SCHEDULER_BLOCKING 장치는 장치 구현 내 작성돼있는 알고리즘에 의해 우선적으로 처리할 상위 레벨 스트림 오브젝트를 선택합니다. 그런 다음 이 스트림 오브젝트와 연관된 파일 설명자로 Read()나 Write()를 호출합니다. 또한 전송을 시작할 파일 내 위치와 요청 전송 크기를 지정하는 AkIOTransferInfo 구조체를 전달하고, 버퍼의 주소로 포인터를 전달해 데이터를 작성하거나 읽어옵니다. 또한 이 장치는, 이 전송의 휴리스틱을 전달합니다. 전송 휴리스틱은 AkIoHeuristics 구조체에 의해 정의되고, 밀리세컨드 단위로 된 작업 시간 및 AK_MIN_PRIORITY와 AK_MAX_PRIORITY 사이 우선 순위 값을 담고 있습니다. 스트리밍 장치의 스케줄러가 생성되어 처리 시간이 가장 짧고 우선 순위가 가장 높은 전송 요청을 Low-Level I/O로 보냅니다.

참고:

Read()나 Write() 안에서 데이터 전송은 완벽하게 실행돼야 합니다. 전송이 성공적으로 완료되면, 함수가 AK_Success 를 반환합니다. 반대의 경우에는 AK_Fail 을 반환합니다.

지연 I/O 연결

AK_SCHEDULER_DEFERRED_LINED_UP 플래그가 지정될 때 Stream Manager가 AK::StreamMgr::IAkIOHookDeferredBatch 인터페이스를 통해 Low-Level I/O 시스템과 상호작용하는 스트리밍 장치를 생성합니다.

지연 인터페이스는 차단 인터페이스보다 좀 더 복잡합니다. 이는 동시에 일어나는 여러 개의 전송 요청을 다루는 Low-Level I/O 구현과 사용하도록 고안되었습니다. 지연 인터페이스는 AK::StreamMgr::IAkIOHookDeferredBatch::BatchRead()AK::StreamMgr::IAkIOHookDeferredBatch::BatchWrite() , AK::StreamMgr::IAkIOHookDeferredBatch::BatchCancel() , 이 세 개의 메소드를 정의합니다. BatchRead() 와 BatchWrite() 는 즉시 반환해야 하며, 스트리밍 장치에게 제공된 콜백 함수를 통해 하나 또는 여러 전송이 언제 완료될 지를 알려줘야 합니다.

사용자는 스트리밍 장치가 Low-Level I/O로 보내게 될 동시 I/O 전송의 최대 개수를 초기화 설정에서 지정하게 됩니다 (AkDeviceSettings::uMaxConcurrentIO).

AK::StreamMgr::IAkIOHookDeferredBatch::BatchRead()AK::StreamMgr::IAkIOHookDeferredBatch::BatchWrite() 로 전송된 각 전송 요청에 대해, 각 발송이 성공했는지를 표시하는 결과 목록을 AK_Success 나 AK_Fail 로 입력해야 합니다. 또한 모든 요청이 성공적으로 발송됐을 경우 AK_Success 를, 어느 한 발송이라도 실패했을 경우 AK_Fail 을 반환해야 합니다. Low-Level I/O 시스템은 AK_Fail 이 반환되는 경우에 한해 발송 결과 목록을 확인해 어느 전송이 실패했는지 알아냅니다.

비슷한 방식으로, 각 전송의 결과 목록을 콜백 함수가 제공해야 하고, 해당하는 전송이 성공적으로 완료됐을 경우 각 값은 AK_Success 여야 하며 그 반대의 경우는 AK_Fail 이어야합니다.

전송을 발송하거나 완료할 때 AK_Fail 이 되면, 해당하는 스트림은 삭제되고 "I/O 오류" 알림이 전송 로그에 나타납니다.

경고: AK::StreamMgr::IAkIOHookDeferredBatch::BatchRead()AK::StreamMgr::IAkIOHookDeferredBatch::BatchWrite() 에서 특정 전송의 발송 결과를 AK_Fail 로 표시할 경우, 스트리밍 장치가 이 전송에 대해 콜백 함수를 기다리지 않기 때문에 이를 호출해서는 안됩니다.

작은 정보: 동시에 많은 요청을 다룰 때 자신의 구현이 더 나은 성능을 낼 경우 AK_SCHEDULER_DEFERRED_LINED_UP 장치가 매우 유용합니다. 그렇지 않으면, AK_SCHEDULER_BLOCKING 장치를 사용해야 합니다. 예를 들어 차단 ReadFile 대신 Win32 OVERLAPPED ReadFile() 을 사용했을 때 장점이 별로 없습니다. ReadFile() 이 I/O 스레드에 의해서 Stream Manager 인터페이스로부터 이미 분리돼 있다는 점을 기억하세요.

반면 지연 장치에는 몇 가지 단점이 있습니다. 지연 장치는 더 많은 메모리를 사용하고, I/O 전송이 차단 장치보다 더 일찍 발행되기 때문에 좀 더 쉽게 삭제되거나 취소되는 경향이 있습니다. 예를 들어, 스케줄러가 한 스트림에 대해 한 번에 여러 전송을 발송할 수 있으며, 사운드 엔진이 SetPosition() 을 호출하면 모든 미해결된 전송이 완료와 함께 삭제됩니다.

참고: AkDeviceSettings::uMaxConcurrentIO 는, 해당 장치가 Low-Level I/O에 발송할 수 있는 전송 요청의 최대 개수를 나타냅니다. 그 외에 AK_SCHEDULER_DEFERRED_LINED_UP 장치의 스케줄러는 AK_SCHEDULER_BLOCKING 스케줄러와 완전히 동일하게 작동합니다. 이는 Stream Manager의 클라이언트가 AK::IAkStdStream::Read()/Write() 를 호출할 때나 실행 중인 자동 스트림의 버퍼링이 버퍼링 대상 이하인 경우에만 전송 요청을 발송하게 됩니다 (AkDeviceSettings::fTargetAutoStmBufferLength, 대상 버퍼링 길이에 대한 자세한 내용은 Audiokinetic Stream Manager 초기화 설정 참고).

스트리밍 장치는 BatchIoTransferItem의 배열을 각 AK::StreamMgr::IAkIOHookDeferredBatch 의 함수로 전달합니다. 이 구조체에는 AkFileDesc와 AkIoHeuristics, AkAsyncIOTransferInfo 를 비롯한 각 전송에 대한 정보가 담겨있습니다. AkAsyncIOTransferInfo 구조체는 앞서 언급한 AkIOTransferInfo 구조체를 확장합니다. AkAsyncIOTransferInfo 에는 읽기나 쓰기를 하는 버퍼 주소가 포함돼있고, pUserData 입력란이 있어 구현하는 사용자가 지연 전송에 메타데이터를 연결할 수 있습니다. AkAsyncIOTransferInfo 구조체는 콜백이 호출될 때까지 존재하게 됩니다. 콜백을 호출한 후에는 이 구조체를 참조해서는 안 됩니다.

읽기나 쓰기를 자신의 I/O 스트리밍 기술로 라우팅할 경우, I/O 요청 우선 순위를 재설정할 때 AkIoHeuristics 에 들어있는 정보가 매우 유용할 수 있습니다.

작은 정보: 기본 Stream Manager의 스케줄러의 구현은 '디스크 대역폭 휴리스틱'이 아니라 '클라이언트 휴리스틱'에 기반하고 있습니다. Stream Manager는 디스크상에 있는 파일들의 레이아웃을 인지하지 못합니다. 자신의 스트리밍 기술이 허용할 경우, 이 지식을 이용해 I/O 요청을 재정렬해 디스크 검색을 최소화합니다.

스트리밍 장치는 가끔 데이터를 삭제해줘야 합니다. 데이터 삭제는 Stream Manager의 클라이언트가 AK::IAkAutoStream::SetPosition() 을 호출하거나반복 재생 휴리스틱을 변경할 때 발생합니다. 때에 따라서는 데이터가 해당 전송이 완료되기도 전에 삭제돼야 할 때도 있습니다. 이러한 삭제는 AkDeviceSettings::uMaxConcurrentIOAkDeviceSettings::fTargetAutoStmBufferLength 가 큰 경우에 발생할 가능성이 높습니다. 지연된 I/O 연결 API에는 도입 지점이 있어 BatchCancel() 이 발생했을 때 알림을 받습니다. 스트리밍 장치가 Low-Level I/O에서 아직 지연 중인 하나 이상의 I/O 전송과 연관된 데이터를 삭제해야할 때, 내부적으로 이 전송들에 'cancelled' 태그를 달고, AK::StreamMgr::IAkIOHookDeferredBatch::BatchCancel() 을 호출한 다음, 콜백이 호출되기를 기다립니다. BatchCancel() 은 Low-Level I/O에 알림을 보내는 데에만 사용되며, 아무 작업을 안 할 수도 있습니다. 스트리밍 장치는 어느 전송이 취소돼야 하는지 알고 있으며, 이를 취소하는 대신 정상적으로 완료하게 하려면 완료와 동시에 삭제하면 됩니다. 어떤 경우라도 콜백 함수는 I/O 전송 정보와 버퍼를 자유롭게 처리할 수 있다는 것을 스트리밍 장치에 알리기 위해 호출돼야 합니다.

경고: 한 전송에 대해 콜백을 두 번 호출하지 않도록 주의하세요.
작은 정보:
  • Low-Level I/O에 큐를 구현할 경우, BatchCancel()을 이용해 요청을 큐에서 제거할 수 있습니다. 큐에서 제거할 수 있다면, BatchCancel() 내에서 콜백 함수를 직접 호출할 수 있습니다.
  • BatchCancel() 내 물리적 장치 컨트롤러를 차단해서는 안 됩니다. 만약 그러면 Stream Manager의 클라이언트를 차단할 수 있습니다.
경고: 취소된 전송의 콜백 함수를 호출할 때 반드시 AK_Success 를 전달해야 합니다. 그 이외의 다른 것들은 I/O 오류로 간주되어 연관 스트림이 종료됩니다.
경고: AK::StreamMgr::IAkIOHookDeferredBatch::BatchCancel() 은 어떤 스레드에서든 호출할 수 있습니다. 따라서 AK::StreamMgr::IAkIOHookDeferredBatch::BatchCancel() 을 구현할 경우 Low-Level I/O 잠금에 대해 특별히 주의해야 합니다. 특히, BatchCancel() 로부터의 pCallback 콜백과 일반 I/O 완료 코드 경로로부터의 콜백 간에 레이스 컨디션이 발생하지 않도록 주의해야 합니다. 더 자세한 내용은 함수의 설명에 나와있습니다.
작은 정보: 반드시 의무적으로 AK::StreamMgr::IAkIOHookDeferredBatch::BatchCancel() 을 구현해야 할 필요는 없습니다. 잠금 문제가 있기 때문에 요청을 정상적으로 완료하는 것보다 취소하는 것이 더 큰 비용을 발생시킬 수 있습니다.

기타 고려 사항

블록 크기 (GetBlockSize())

앞서 언급한 것처럼 Stream Manager 사용자는 허용된 전송 크기와 관련해 하위 레벨 I/O 제약을 고려해야 합니다. 가장 일반적인 제약은 일정 값의 배수로 크기가 정해진다는 것입니다. 이 값은 파일 설명자에 대해 AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() 로 반환됩니다. 예를 들어, Windows®에서 FILE_FLAG_NO_BUFFERING 플래그로 연 파일은 해당 섹터 크기의 배수 크기로 읽어야 합니다. AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() 메소드는 섹터 크기를 반환합니다. 그러나 만약 반대로 Win32 파일을 이 AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() 플래그로 열지 않는 경우에는 1을 반환하여 Stream Manager 클라이언트를 제한하지 않도록 합니다.

경고: AK::StreamMgr::IAkLowLevelIOHook::GetBlockSize() 는 절대로 0을 반환해서는 안 됩니다.
작은 정보: 하위 레벨 블록 크기의 제약을 다루는 책임은 Stream Manager의 클라이언트에게로 전달됩니다. 블록 크기가 클 수록 사운드 엔진이 스트리밍 데이터를 더 낭비하게 됩니다. 플랫폼의 I/O 시스템에 특정 정렬 제약이 있거나 I/O 대역폭 성능을 크게 높일 수 있는 경우를 제외하고는 하위 레벨 블록 크기를 1로 사용해야 합니다.

프로파일링

AK::StreamMgr::IAkLowLevelIOHook::GetDeviceDesc() 는 Wwise에서 프로파일링에 사용됩니다. Low-Level I/O의 기본 구현의 정보는 Wwise에서 프로파일링할 때 나타나는 정보입니다.

AK::StreamMgr::IAkLowLevelIOHook::GetDeviceData() 는 비슷하지만 모든 프로파일링 프레임에 호출된다는 점이 다릅니다. 반환되는 값은 Streaming Device 탭의 Custom Parameter 열에 나타납니다.

샘플 기본 구현 설명

Low-Level I/O의 기본 구현은 Wwise SDK에 제공되고 있습니다. samples/SoundEngine/ 디렉터리에서 찾아볼 수 있습니다.

클래스 개요

아래 그림은 Low-Level I/O 예제와 Low-Level I/O API와의 관계를 나타낸 클래스 다이어그램입니다.

CAkDefaultIOHookBlocking은 File Location Resolver API (AK::StreamMgr::IAkFileLocationResolver)와 차단 I/O 연결 (AK::StreamMgr::IAkIOHookBlocking)을 구현합니다. CAkDefaultIOHookDeferred 도 동일하지만, 지연 I/O 연결을 구현하거나 (AK::StreamMgr::IAkIOHookDeferredBatch) 일괄 처리하지 않은 지연 I/O 연결에 어댑터 인터페이스를 구현한다는 점이 다릅니다 (AK::StreamMgr::IAkIOHookDeferred). 단일 장치 I/O 시스템에서 이 두 구현 중 어느 것이든 하나를 사용하면 됩니다. CAkDefaultIOHookBlocking::Init()CAkDefaultIOHookDeferred::Init() 는 둘 다 Stream Manager에 스트리밍 장치를 생성하고, 이를 장치 설정으로 전달해 반환된 장치 ID를 저장합니다. 한 가지 다른 점은, 성공적인 초기화를 위해 CAkDefaultIOHookBlocking::Init()로는 AK_SCHEDULER_BLOCKING 스케줄러 타입을 전달해야 하는 반면, CAkDefaultIOHookDeferred::Init()로는 AK_SCHEDULER_DEFERRED_LINED_UP을 전달해야 한다는 것입니다.

또한 이 두 장치는 Stream Manager에 스스로를 유일한 File Location Resolver로 등록합니다. 그러나 이는 Stream Manager 에 등록돼있는 File Location Resolver가 없는 경우에 한합니다.

다음 그림은 단일 장치 I/O 시스템을 나타낸 블록 다이어그램입니다. 'Low-Level I/O'는 File Location Resolver API뿐만 아니라 I/O 연결 API 중 하나를 구현하는 클래스입니다. 다음 예제 클래스 중 어느 것이든 될 수 있습니다.

  • CAkDefaultIOHookBlocking
  • CAkDefaultIOHookDeferred
  • CAkFilePackageIOHookBlocking
  • CAkFilePackageIOHookDeferred

다음은 CAkDefaultIOHookBlocking만을 사용해 (오류 처리 없이) I/O 시스템을 초기화하는 방법입니다.

// Stream Manager를 생성합니다.
AkStreamMgrSettings stmSettings;
AK::StreamMgr::Create( stmSettings );
// 차단 하위 레벨 I/O 핸드셰이킹으로 스트리밍 장치를 생성합니다.
AkDeviceSettings deviceSettings;
CAkDefaultIOHookBlocking lowLevelIO;
// File Location Resolver가 아직 정의돼있지 않은 경우, Init이 lowLevelIO를 File Location Resolver로 등록하고 스트리밍 장치를 생성합니다.
lowLevelIO.Init( deviceSettings );

CAkDefaultIOHookDeferred 장치를 사용하고자 할 경우, CAkDefaultIOHookBlockingCAkDefaultIOHookDeferred로 교체하고, AK_SCHEDULER_BLOCKING을 AK_SCHEDULER_DEFERRED_LINED_UP으로 교체합니다.

두 장치 내의 File Location Resolver 구현은 이 두 장치 모두 상속받은 CAkFileLocationBase의 서비스를 사용합니다. CAkFileLocationBase에 구현된 파일 위치 방식에 대한 더 자세한 정보는 아래의 기본 파일 위치 을 참고하세요.

지연 \I/O 연결과 기본 차단의 구현에 대한 더 자세한 정보는 차단 I/O 연결 설명 섹션과 아래의 지연 I/O 연결 설명 을(를) 참고하세요.

템플릿으로 작성된 또 다른 클래스는, AK::StreamMgr::IAkFileLocationResolverAK::StreamMgr::IAkLowLevelIOHook 를 구현하는 클래스가 파일 패키지를 관리할 수 있도록 합니다 (CAkDefaultIOHookBlockingCAkDefaultIOHookDeferred와 유사). CAkFilePackageLowLevelIO<>가 이 클래스입니다. 파일 패키지는 AK File Packager 유틸리티를 이용해 생성된 파일들입니다. Low-Level I/O의 파일 패키지 관리에 대한 더 자세한 정보는 아래의 예제 파일 패키지 Low-Level I/O 구현 설명 섹션을 참고하세요. CAkFilePackageIOHookBlockingCAkFilePackageIOHookDeferred 클래스는 CAkDefaultIOHookBlockingCAkDefaultIOHookDeferred의 구체적인 정의이며, 각각 파일 패키지 관리를 통해 강화됩니다.

둘 이상의 장치로 I/O 시스템을 구현하고자 할 경우 Stream Manager에 각각 별도의 File Location Resolver를 등록해야 하며, 적합한 장치로 파일 관리를 발송하는 역할을 합니다. SDK는 이 기능을 구현할 때 사용할 밑그림으로 CAkDefaultLowLevelIODispatcher를 제공합니다. 다중 장치 I/O 시스템에 대한 더 자세한 정보는 다중 장치 I/O 시스템 를 참고하세요.

기본 파일 위치

사운드 엔진이 사용하는 파일은 ID(스트리밍된 오디오 파일과 SoundBank에 사용)나 문자열(일반적으로 SoundBank를 위해 예약됨)로 열게 됩니다. CAkDefaultIOHook[Blocking|Deferred]CAkFileLocationBase로부터 상속되며, 전역적 경로를 설정하도록 메소드를 나타냅니다 (SetBasePath(), AddBasePath(), SetBankPath(), SetAudioSrcPath()). CAkDefaultIOHook[Blocking|Deferred]::Open()의 두 오버로드는 CAkFileLocationBase::GetFullFilePath()를 호출하여 네이티브 파일 열기 함수로 사용할 수 있는 전체 파일 이름을 생성합니다. 먼저 기본 경로가 맨 앞에 추가됩니다. 그런 다음, 파일이 SoundBank일 경우 해당 SoundBank 경로가 추가됩니다. 만약 스트리밍된 오디오 파일일 경우, 오디오 음원 경로가 추가됩니다. 어느 경우든, 현재 언어에 의존적인 위치의 파일일 경우에는 해당 언어 디렉터리 이름이 추가됩니다.

문자열 오버로드를 사용할 경우, 파일 이름 문자열이 이 경로 끝에 추가됩니다.

ID 오버로드에서 Audiokinetic의 파일 ID만 결정됩니다. ID 오버로드를 사용하는 게임은 해당 ID 매핑 체계에 맞게 구현을 변경해줘야 합니다. CAkFileLocationBase의 매핑 체계는, 파일 ID를 기반으로 문자열을 생성하고, 파일 타입에 따른 확장자를 끝에 추가하는 방식입니다 (Codec ID로 지정됨). 이는 'Copy Streamed Files' 생성 직후 단계 (post-generation step) 사운드뱅크 설정이 사용하는 스트리밍 파일 명명 규칙과 호환됩니다. 사운드뱅크 설정에 대한 더 자세한 정보는 Wwise 도움말을 참고하세요.

참고: SoundBank 설정의 'Use SoundBank names' 옵션이 선택돼있지 않은 경우, Wwise는 [ID].bnk 포맷에 있는 이름으로 뱅크 파일을 생성합니다. 따라서 ID에 의한 명시적 뱅크 로드(AK::SoundEngine::LoadBank()의 ID 오버로드이용)와 AK::SoundEngine::PrepareEvent()로부터 트리거되는 암묵적 뱅크 로드는 기본 Low-Level I/O에 올바르게 매핑됩니다. 'Use SoundBank names' 옵션이 선택된 경우, Wwise는 원본 이름으로 뱅크 파일을 생성합니다 (bank_name.bnk). 암묵적 뱅크 로딩과 문자열에 의한 명시적 뱅크 로딩이 올바르게 매핑됩니다. 그러나 ID에 의한 명시적 뱅크 로딩은 제대로 작동하지 않습니다. 기본 Low-Level I/O가 존재하지 않는 [ID].bnk 이름으로 파일을 열려고 하기 때문입니다.

SDK 관점에서의 'Use SoundBank names' 옵션에 대한 정보는 SoundBank 이름 사용하기 를 참고하세요. 일반적인 SoundBank 설정에 대한 정보는 Wwise 도움말을 참고하시면 됩니다.

같은 방식으로, 기본 Low-Level I/O는 [ID].[ext] 포맷의 이름으로 스트리밍 오디오 파일을 엽니다. [ext]는 오디오 형식에 따른 확장자입니다. Wwise가 SoundBank 생성 끝에 Generated SoundBank 경로로 [ID].[ext] 파일 이름 형식을 이용해 모든 스트리밍 오디오 파일을 자동으로 복사하게 할 수 있습니다 ( 스트림된 오디오 파일 과 Wwise 도움말 참고).

전체 파일 경로가 생긴 다음, CAkDefaultIOHook[Blocking|Deferred]::Open()이 시스템 API(특정 플랫폼 전용 예제 파일 AkFileHelpers.h에 구현된 헬퍼로 감싸져있음)를 이용해 직접 파일을 엽니다.

위에서 언급한 CAkFileLocationBase 메소드를 이용해 게임 코드에서 기본 사항 및 SoundBank, 오디오 음원, 특정 언어 경로를 설정할 수 있습니다. 더 자세한 정보는 기본 Low-Level I/O 구현 의 예제 코드를 참고하세요.

작은 정보: 사운드 엔진은 특정 언어 전용 디렉터리에서 뱅크를 언제 로드해야하는지 알지 못합니다. 따라서 true로 설정된 AkFileSystemFlags 구조체의 bIsLanguageSpecific 플래그로 항상 AK::IAkStreamMgr::CreateStd() 를 호출하고, 만약 처음 호출이 실패하면 false를 설정합니다. Low-Level I/O의 기본 예제 구현은 현재 특정 언어 전용 디렉터리로부터 무작정 파일을 열려고 합니다. 이는 실패한 fopen() 호출때문에 매우 비효율적인 처리 방식이 되기 때문에 피해야 합니다.

항상 필요에 맞게 Low-Level I/O를 재구현해야합니다. 특정 언어 전용 SoundBank의 이름을 알 경우나, 이들을 식별하기 위해 명명하는 방식을 정의한 경우, 처리 과정의 앞단계에서 올바른 폴더로부터 이를 로드해야 합니다.

지연 열기의 기본 구현

io_bSyncOpen flag에 대한 자세한 내용은 지연 열기 를 참고하세요.

모든 기본 I/O 연결(차단 및 지연)은 in_bAsyncOpen 플래그로 초기화됩니다 (CAkDefaultIOHook[Blocking|Deferred]::Init() 참고). 만약 이게 true면, 가능한 때에 파일이 비동기식으로 열립니다. 즉, in_bAsyncOpen 이 true 이면서 동시에 AK::StreamMgr::IAkFileLocationResolver::Open() 의 io_bSyncOpen 인자가 false이면, 파일 열기가 지연됩니다. 이 경우, AkFileDesc 구조체의 deviceID 영역만 설정되고, 곧바로 메소드가 반환됩니다.

차단 I/O 연결 설명

모든 플랫폼에서 CAkDefaultIOHookBlocking::Read()CAkDefaultIOHookBlocking::Write() 구현은, AkIOTransferInfo 에 있는 정보를 이용해 네이티브 파일 읽기/쓰기 함수의 차단 호출을 수행합니다. 해당 작업이 성공할 경우 함수는 AK_Success를 반환합니다.

지연 I/O 연결 설명

지연 I/O 연결 구현은 차단 I/O 연결보다 조금 더 까다롭습니다. 일반적으로 플랫폼의 비동기 파일 읽기 API는 특정 플랫폼 전용 구조체를 fread()로 전달하고 (Windows에서는 OVERLAPPED), I/O가 완료됐다고 알리는 콜백 함수가 호출될 때가지 전체 I/O 작업 시간 동안 이를 유지합니다.

구현은 모든 플랫폼이 비슷합니다. CAkDefaultIOHookDeferred는 이 특정 플랫폼 전용 구조체의 배열을 해당 메모리 풀에 할당합니다. CAkDefaultIOHookDeferred::Read()가 호출되면, 비어있는 첫 번째 구조체를 찾아 'used'라고 표시하고, AkAsyncIOTransferInfo에 있는 정보로 채워넣은 다음, fread()로 전달합니다. 또한 서명이 플랫폼의 비동기 fread() 함수와 호환되는 로컬 정적 콜백 함수를 전달합니다. 이 함수에서 작업이 성공적이었는지를 판단하며, 특정 플랫폼 전용 I/O 구조체를 배포하고 스트리밍 장치를 호출합니다.

Read()/Write()와 시스템의 콜백 간 경쟁 상태를 피해야 하기 때문에 배열에서 특정 플랫폼 전용 I/O 구조체를 구하고 배포하는 일은 극미합니다.

일부 플랫폼에는 이미 커널에 전송된 I/O 요청을 취소하는 기능이 있습니다. 이 경우, CAkDefaultIOHookDeferred::Cancel()에서 호출됩니다. Windows에서는 CancelIO()가 파일 핸들에 대해 모든 요청을 취소합니다. 따라서, 스트리밍 장치가 이 함수를 호출하기 전에 io_bCancelAllTransfersForThisFile 인자를 확인해 해당 파일의 모든 요청을 취소하도록 해야 합니다. 그렇지 않을 경우, Cancel()이 아무 작업도 하지 않으므로 요청이 완료될 때까지 기다립니다. 스트리밍 장치는 어느 것을 제거해야 하는지 알고 있습니다.

경고: 스트리밍 장치에 의해 명시적으로 취소되지 않은 요청을 취소하지 마세요. 만약 이러한 요청을 취소할 경우, 유효하지 않거나 손상된 데이터가 전달되고 사운드 엔진을 강제 종료시킬 수 있습니다.
경고: CAkDefaultIOHookDeferred 는 네이티브 비동기 파일 읽기 함수를 사용하며 자신의 지연 I/O 연결을 구현하는 데 쓸 수 있는 밑그림 목적으로만 제공됩니다. 수정하지 않은 채 그대로 사용하지 마시기 바랍니다. 네이티브 차단 호출을 사용하는 것보다 더 좋은 성능을 낼 수 없습니다. 사실 심지어 더 좋지 않은 성능을 낼 수도 있습니다. 지연 I/O 연결 섹션의 참고란을 보면 자세한 설명이 나와있습니다. 지연 하위 레벨 API의 목적은, 자신의 스트리밍 기술을 이용해 더 효율적으로 I/O 요청을 다룰 수 있게 하는 것입니다 차단 I/O 후크 대신 지연 I/O 후크 사용하기 를 참고하면 어떤 연결이 자신의 작업에 더 적합한 지 알 수 있습니다.

다중 장치 I/O 시스템

다음은 다중 장치 I/O 시스템을 나타낸 그림입니다.

여러 개의 스트리밍 장치로 작업할 때는, 장치의 하위 레벨 I/O 연결과는 전혀 별개로 File Location Resolver를 인스턴스화하고 등록해야 합니다. 그 목적은, 적절한 장치로 파일을 발송하기 위해서입니다. 어느 장치가 어느 파일을 다룰 것인지를 결정하는 방식은 직접 정의하기 나름입니다. 밑그림으로 CAkDefaultLowLevelIODispatcher를 사용할 수도 있습니다. 기본 구현에서는 브루트 포스(brute force) 기법을 사용합니다. 즉, 성공할 때까지 파일을 열도록 각 장치에 요청합니다. 따라서 이 장치들도 AK::StreamMgr::IAkFileLocationResolver 인터페이스를 구현해야 합니다. 이 SDK에 제공된 어떤 예제를 사용하든 상관 없습니다.

  • CAkDefaultIOHookBlocking
  • CAkDefaultIOHookDeferred
  • CAkFilePackageIOHookBlocking
  • CAkFilePackageIOHookDeferred

다음은 지연 장치와 파일 패키지 차단 장치를 다중 장치 시스템에서 인스턴스화하는 방법입니다 (그러나 실제 작업에서는 이 예제가 큰 도움이 되지 않습니다).

// Stream Manager를 생성합니다.
AkStreamMgrSettings stmSettings;
AK::StreamMgr::Create( stmSettings );
// File Location Resolver를 생성하고 등록합니다.
CAkDefaultLowLevelIODispatcher lowLevelIODispatcher;
AK::StreamMgr::SetFileLocationResolver( &lowLevelIODispatcher );
// 지연 장치를 생성합니다.
CAkDefaultIOHookDeferred hookIODeferred;
AkDeviceSettings deviceSettings1;
deviceSettings1.uSchedulerTypeFlag = AK_SCHEDULER_DEFERRED_LINED_UP;
hookIODeferred.Init( deviceSettings1 );
// 전역 File Location Resolver에 추가합니다.
lowLevelIODispatcher.AddDevice( hookIODeferred );
// 파일 패키지 관리로 차단 장치를 생성합니다.
CAkFilePackageIOHookBlocking hookIOBlockingFP;
AkDeviceSettings deviceSettings2;
deviceSettings2.uSchedulerTypeFlag = AK_SCHEDULER_BLOCKING;
hookIOBlockingFP.Init( deviceSettings2 );
// 전역 File Location Resolver에 추가합니다.
lowLevelIODispatcher.AddDevice( hookIOBlockingFP );
작은 정보: 다중 장치는 반드시 다중 물리적 장치와 함께 사용해야 합니다.

예제 파일 패키지 Low-Level I/O 구현 설명

일반적인 설명

CAkFilePackageLowLevelIO<> 클래스는 기본 하위 레벨 I/O 연결 위의 레이어입니다. 이 클래스는 예제 File Packager로 생성된 파일을 로드할 수 있게 함으로써 기본 하위 레벨 I/O 연결을 확장합니다 ( File Packager 유틸리티 참고). 이는 ID를 파일 설명자로 처리하는 더 고급 방식을 사용합니다. 파일 패키지는 연결된 파일들 (스트리밍 오디오 파일 및 뱅크 파일)로 구성돼있으며, 헤더에는 이 파일들의 정보가 들어있습니다.

예제 코드는 파일 패키지 Low-Level I/O 구현 을 참고하세요. File Package Low-Level I/O를 있는 그대로 (CAkFilePackageLowLevelIO[Blocking|Deferred]), File Packager 유틸리티와 사용해도 됩니다. 또는, 이를 단순히 고급 파일 위치 결정 메소드 구현의 개념 증명(proof of concept) 용도로만 생각해도 됩니다.

File Package Low-Level I/O는 CAkFilePackageLowLevelIO::LoadFilePackage()를 표시하며, 해당 인자는 예제 File Packager로 생성된 패키지의 파일 이름입니다. 기본 구현 서비스를 이용하여 연 다음, 헤더를 파싱하고 조회 테이블을 생성합니다. 파일 패키지의 개수는 원하는 만큼 로드할 수 있습니다. LoadFilePackage()는 ID를 반환하며, UnloadFilePackage()를 사용해 이를 언로드할 수 있습니다.

CAkFilePackage 클래스는 로드된 파일 패키지를 나타내며, 파일 조회를 다루는 모든 데이터 구조체와 코드는 CAkFilePackageLUT 클래스에 정의돼있습니다. CAkFilePackageLowLevelIO<> 클래스는 기본 I/O 연결 메소드 중 일부를 오버라이드하여, CAkFilePackageLUT 조회 기능을 작동시킵니다. 파일 설명자를 찾지 못할 경우나, 요청이 파일 패키지에 속한 파일 설명자를 고려하지 않을 경우, 기본 구현이 호출됩니다.

파일 위치

패키지 내 파일 조회의 목적은, 플랫폼의 파일 열기 함수에서 파일 핸들을 한 번만 구한 후, AK::StreamMgr::IAkFileLocationResolver::Open() 이 호출될 때마다, 파일 패키지 내 원본 파일의 오프셋에 해당하는 값으로 이 파일 핸들을 이용하는 파일 설명자를 반환하기만 하면 되게끔 하는 것입니다 (AkFileDesc 파일 설명자의 uSector 영역 사용). 이렇게 하면 디스크 상의 파일 배치를 좀 더 쉽게 통제할 수 있습니다.

File Packager 유틸리티가 면밀하게 헤더를 준비하면 Low-Level I/O는 여기 들어있는 조회 테이블을 구하기 위해 몇몇 포인터를 캐스팅하기만 하면 됩니다. 이 조회 테이블 중 하나는 스트리밍 오디오 파일용이고 다른 하나는 뱅크 파일용입니다. 조회 테이블은 다음 구조체들의 배열입니다.

struct AkFileEntry
{
AkFileID fileID; // 파일 식별자.
AkUInt32 uBlockSize; // 한 블록의 크기, 정렬 필요 (바이트 단위).
AkInt64 iFileSize; // 파일 크기 (단위: 바이트).
AkUInt32 uStartBlock;// 차단 시작, uBlockSize 로 표현됨.
AkUInt32 uLanguageID;// 언어 ID. AK_INVALID_LANGUAGE_ID 특정 언어 지정이 없는 경우.
};

테이블 키는 파일의 fileID입니다. 서로 다른 언어의 해당 파일들은 fileID는 같지만, uLanguageID는 다릅니다. File Packager는 항상 파일 항목을 fileID로 먼저 정렬하고 그 다음에 uLanguageID로 정렬합니다. CAkFilePackageLowLevelIO::Open()에서, ID는 CAkFilePackageLUT::LookupFile()로 전달됩니다 (Open()의 문자열 버전에서는, 사운드 엔진 API의 AK::SoundEngine::GetIDFromString() 기능을 이용해 먼저 문자열이 해시됩니다). CAkFilePackageLUT::LookupFile()은 플래그의 uCodecID를 기반으로 검색에 적합한 테이블을 선택해 fileID와 uLanguageID 키로 바이너리 검색을 수행합니다. 일치하는 파일을 찾으면, 해당 파일 항목의 주소가 CAkFilePackageLowLevelIO::Open()으로 반환되고 필요한 정보를 모아 파일 설명자에 입력합니다 (AkFileDesc).

작은 정보: 일치하는 파일이 나올 때까지 각 파일 패키지을 검색합니다. 한 파일 패키지를 오직 사운드뱅크와 사용하고, 다른 파일 패키지는 스트리밍 파일하고만 사용할 경우, AkFileSystemFlags 를 사용하는 구현을 변경해 올바른 파일 패키지에서만 파일을 검색하도록 해야 합니다.

파일 설명자의 핸들인 hFile은 파일 패키지의 핸들입니다. 파일 크기인 iFileSize와 시작 블록인 uSector는 파일 항목에 직접 저장되어있습니다.

참고: Stream Manager는, hFile 핸들로 표현된 파일의 시작으로 부터 바이트 오프셋이 아닌 블록(sector) 오프셋을 요구한다는 것을 기억하세요. 블록 크기는 파일 위치의 단위를 나타냅니다. File Packager 현재 버전은 모든 파일에 대해 동일한 블록을 사용하며, 이는 생성 시점에 지정됩니다 (-blocksize 스위치 이용. File Packager의 명령줄 인자에 대한 더 자세한 내용은 Wwise 도움말 참고). 0으로 채우기(zero-padding)를 수행해, 연결된 파일들이 항상 블록 경계에서 시작하게 합니다.

File Package 하위 레벨 I/O는 파일 설명자의 uCustomParamSize 영역을 이용해 블록 크기를 저장합니다. 여기에는 두 가지 목적이 있습니다.

  • 해당 블록 크기에 쉽게 접근합니다.
  • 파일 패키지에 속한 파일 설명자를 구분합니다 (uCustomParamSize == 0은 패키지에서 파일이 검색되지 않았음을 뜻함). 예를 들어, 파일이 패키지의 한 부분일 경우, CAkFilePackageLowLevelIO::Close() 로 파일 핸들이 닫히지 않습니다.

언어 관리하기

Wwise 버전 2011.2부터, AK::StreamMgr::SetCurrentLanguage()로 기본 Stream Manager 모듈에 현재 언어가 설정돼있고 이는 AkStreamMgrModule.h에 정의돼있습니다. 맨 끝에 붙는 슬래시나 역슬래시 없이 언어 이름을 전달합니다.

CAkFileLocationBase로부터 상속된 기본 하위 레벨 I/O 구현은 언어 이름을 Stream Manager로부터 가져와 기본 경로 뒤에 붙입니다. 따라서 언어 이름은, 이 특정 언어에 대해 현지화된 에셋이 저장돼 있는 디렉터리 이름과 같아야 합니다.

File Packager 유틸리티가 생성한 파일 패키지에는 다양한 언어로 돼있는 동일한 에셋의 하나 또는 여러 버전이 있을 수 있습니다. 이들 헤더에는 언어 이름의 문자열 맵이 있습니다. File Package Low-Level I/O는 Stream Manager에서 언어가 바뀌는 것을 듣고, 현재 언어 이름을 사용해 패키징된 현지화 에셋의 올바른 현지화 버전을 검색합니다.

AKSOUNDENGINE_API void GetDefaultSettings(AkStreamMgrSettings &out_settings)
AkUInt32 uSchedulerTypeFlags
Scheduler type flags.
AkUInt32 AkFileID
Integer-type file identifier
Definition: AkTypes.h:81
int64_t AkInt64
Signed 64-bit integer
Definition: AkTypes.h:99
#define AK_SCHEDULER_BLOCKING
#define AK_SCHEDULER_DEFERRED_LINED_UP
AKSOUNDENGINE_API IAkStreamMgr * Create(const AkStreamMgrSettings &in_settings)
AKSOUNDENGINE_API void GetDefaultDeviceSettings(AkDeviceSettings &out_settings)
AKSOUNDENGINE_API void SetFileLocationResolver(IAkFileLocationResolver *in_pFileLocationResolver)
uint32_t AkUInt32
Unsigned 32-bit integer
Definition: AkTypes.h:85

이 페이지가 도움이 되었나요?

지원이 필요하신가요?

질문이 있으신가요? 문제를 겪고 계신가요? 더 많은 정보가 필요하신가요? 저희에게 문의해주시면 도와드리겠습니다!

지원 페이지를 방문해 주세요

작업하는 프로젝트에 대해 알려주세요. 언제든지 도와드릴 준비가 되어 있습니다.

프로젝트를 등록하세요. 아무런 조건이나 의무 사항 없이 빠른 시작을 도와드리겠습니다.

Wwise를 시작해 보세요