| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <FileIOHandler_wwise.h>
- #include <AzCore/Casting/numeric_cast.h>
- #include <AzCore/IO/FileIO.h>
- #include <AzCore/IO/IStreamer.h>
- #include <AzCore/IO/Streamer/FileRequest.h>
- #include <AzCore/Debug/Profiler.h>
- #include <IAudioInterfacesCommonData.h>
- #include <AkPlatformFuncs_Platform.h>
- #include <AudioEngineWwise_Traits_Platform.h>
- #include <cinttypes>
- #define MAX_NUMBER_STRING_SIZE (10) // max digits in u32 base-10 number
- #define MAX_EXTENSION_SIZE (4) // .xxx
- #define MAX_FILETITLE_SIZE (MAX_NUMBER_STRING_SIZE + MAX_EXTENSION_SIZE + 1) // null-terminated
- namespace Audio
- {
- // AkFileHandle must be able to store our AZ::IO::HandleType
- static_assert(sizeof(AkFileHandle) >= sizeof(AZ::IO::HandleType), "AkFileHandle must be able to store at least the size of a AZ::IO::HandleType");
- namespace Platform
- {
- AkFileHandle GetAkFileHandle(AZ::IO::HandleType realFileHandle);
- AZ::IO::HandleType GetRealFileHandle(AkFileHandle akFileHandle);
- void SetThreadProperties(AkThreadProperties& threadProperties);
- }
- AkFileHandle GetAkFileHandle(AZ::IO::HandleType realFileHandle)
- {
- if (realFileHandle == AZ::IO::InvalidHandle)
- {
- return InvalidAkFileHandle;
- }
- return Platform::GetAkFileHandle(realFileHandle);
- }
- AZ::IO::HandleType GetRealFileHandle(AkFileHandle akFileHandle)
- {
- if (akFileHandle == InvalidAkFileHandle)
- {
- return AZ::IO::InvalidHandle;
- }
- return Platform::GetRealFileHandle(akFileHandle);
- }
- CBlockingDevice_wwise::~CBlockingDevice_wwise()
- {
- Destroy();
- }
- bool CBlockingDevice_wwise::Init(size_t poolSize)
- {
- Destroy();
- AkDeviceSettings deviceSettings;
- AK::StreamMgr::GetDefaultDeviceSettings(deviceSettings);
- deviceSettings.uIOMemorySize = aznumeric_cast<AkUInt32>(poolSize);
- deviceSettings.uSchedulerTypeFlags = AK_SCHEDULER_BLOCKING;
- Platform::SetThreadProperties(deviceSettings.threadProperties);
- m_deviceID = AK::StreamMgr::CreateDevice(deviceSettings, this);
- return m_deviceID != AK_INVALID_DEVICE_ID;
- }
- void CBlockingDevice_wwise::Destroy()
- {
- if (m_deviceID != AK_INVALID_DEVICE_ID)
- {
- AK::StreamMgr::DestroyDevice(m_deviceID);
- m_deviceID = AK_INVALID_DEVICE_ID;
- }
- }
- bool CBlockingDevice_wwise::Open(const char* filename, AkOpenMode openMode, AkFileDesc& fileDesc)
- {
- AZ::IO::OpenMode azOpenMode = AZ::IO::OpenMode::ModeBinary;
- switch (openMode)
- {
- case AK_OpenModeRead:
- azOpenMode |= AZ::IO::OpenMode::ModeRead;
- break;
- case AK_OpenModeWrite:
- azOpenMode |= AZ::IO::OpenMode::ModeWrite;
- break;
- case AK_OpenModeWriteOvrwr:
- azOpenMode |= (AZ::IO::OpenMode::ModeUpdate | AZ::IO::OpenMode::ModeWrite);
- break;
- case AK_OpenModeReadWrite:
- azOpenMode |= (AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeWrite);
- break;
- default:
- AZ_Assert(false, "Unknown Wwise file open mode.");
- return false;
- }
- auto fileIO = AZ::IO::FileIOBase::GetInstance();
- if (AZ::u64 fileSize = 0;
- fileIO->Size(filename, fileSize) && fileSize != 0)
- {
- AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
- fileIO->Open(filename, azOpenMode, fileHandle);
- if (fileHandle != AZ::IO::InvalidHandle)
- {
- fileDesc.hFile = GetAkFileHandle(fileHandle);
- fileDesc.iFileSize = aznumeric_cast<AkInt64>(fileSize);
- fileDesc.uSector = 0;
- fileDesc.deviceID = m_deviceID;
- fileDesc.pCustomParam = nullptr;
- fileDesc.uCustomParamSize = 0;
- return true;
- }
- }
- return false;
- }
- AKRESULT CBlockingDevice_wwise::Read(AkFileDesc& fileDesc, const AkIoHeuristics&, void* buffer, AkIOTransferInfo& transferInfo)
- {
- AZ_Assert(buffer, "Wwise didn't provide a valid desination buffer to Read into.");
- AZ::IO::HandleType fileHandle = GetRealFileHandle(fileDesc.hFile);
- auto fileIO = AZ::IO::FileIOBase::GetInstance();
- AZ::u64 currentFileReadPos = 0;
- fileIO->Tell(fileHandle, currentFileReadPos);
- if (currentFileReadPos != transferInfo.uFilePosition)
- {
- fileIO->Seek(fileHandle, aznumeric_cast<AZ::s64>(transferInfo.uFilePosition), AZ::IO::SeekType::SeekFromStart);
- }
- AZ::u64 bytesRead = 0;
- fileIO->Read(fileHandle, buffer, aznumeric_cast<AZ::u64>(transferInfo.uRequestedSize), false, &bytesRead);
- const bool readOk = (bytesRead == aznumeric_cast<AZ::u64>(transferInfo.uRequestedSize));
- AZ_Assert(readOk,
- "Number of bytes read (%llu) for read request doesn't match the requested size (%u).",
- bytesRead, transferInfo.uRequestedSize);
- return readOk ? AK_Success : AK_Fail;
- }
- AKRESULT CBlockingDevice_wwise::Write(AkFileDesc& fileDesc, const AkIoHeuristics&, void* data, AkIOTransferInfo& transferInfo)
- {
- AZ_Assert(data, "Wwise didn't provide a valid source buffer to Write from.");
- AZ::IO::HandleType fileHandle = GetRealFileHandle(fileDesc.hFile);
- auto fileIO = AZ::IO::FileIOBase::GetInstance();
- AZ::u64 currentFileWritePos = 0;
- fileIO->Tell(fileHandle, currentFileWritePos);
- if (currentFileWritePos != transferInfo.uFilePosition)
- {
- fileIO->Seek(fileHandle, aznumeric_cast<AZ::s64>(transferInfo.uFilePosition), AZ::IO::SeekType::SeekFromStart);
- }
- AZ::u64 bytesWritten = 0;
- fileIO->Write(fileHandle, data, aznumeric_cast<AZ::u64>(transferInfo.uRequestedSize), &bytesWritten);
- const bool writeOk = (bytesWritten == aznumeric_cast<AZ::u64>(transferInfo.uRequestedSize));
- AZ_Error("Wwise", writeOk,
- "Number of bytes written (%llu) for write request doesn't match the requested size (%u).",
- bytesWritten, transferInfo.uRequestedSize);
- return writeOk ? AK_Success : AK_Fail;
- }
- AKRESULT CBlockingDevice_wwise::Close(AkFileDesc& fileDesc)
- {
- auto fileIO = AZ::IO::FileIOBase::GetInstance();
- return fileIO->Close(GetRealFileHandle(fileDesc.hFile)) ? AK_Success : AK_Fail;
- }
- AkUInt32 CBlockingDevice_wwise::GetBlockSize([[maybe_unused]] AkFileDesc& fileDesc)
- {
- // No constraint on block size (file seeking).
- return 1;
- }
- void CBlockingDevice_wwise::GetDeviceDesc(AkDeviceDesc& deviceDesc)
- {
- deviceDesc.bCanRead = true;
- deviceDesc.bCanWrite = true;
- deviceDesc.deviceID = m_deviceID;
- AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "IO::IArchive", AZ_ARRAY_SIZE(deviceDesc.szDeviceName));
- deviceDesc.uStringSize = aznumeric_cast<AkUInt32>(AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName));
- }
- AkUInt32 CBlockingDevice_wwise::GetDeviceData()
- {
- return 1;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- CStreamingDevice_wwise::~CStreamingDevice_wwise()
- {
- Destroy();
- }
- bool CStreamingDevice_wwise::Init(size_t poolSize)
- {
- Destroy();
- AkDeviceSettings deviceSettings;
- AK::StreamMgr::GetDefaultDeviceSettings(deviceSettings);
- deviceSettings.uIOMemorySize = aznumeric_cast<AkUInt32>(poolSize);
- deviceSettings.uSchedulerTypeFlags = AK_SCHEDULER_DEFERRED_LINED_UP;
- Platform::SetThreadProperties(deviceSettings.threadProperties);
- m_deviceID = AK::StreamMgr::CreateDevice(deviceSettings, this);
- return m_deviceID != AK_INVALID_DEVICE_ID;
- }
- void CStreamingDevice_wwise::Destroy()
- {
- if (m_deviceID != AK_INVALID_DEVICE_ID)
- {
- AK::StreamMgr::DestroyDevice(m_deviceID);
- m_deviceID = AK_INVALID_DEVICE_ID;
- }
- }
- bool CStreamingDevice_wwise::Open(const char* filename, [[maybe_unused]] AkOpenMode openMode, AkFileDesc& fileDesc)
- {
- AZ_Assert(openMode == AK_OpenModeRead, "Wwise Async File IO - Only supports opening files for reading.\n");
- auto fileIO = AZ::IO::FileIOBase::GetInstance();
- if (AZ::u64 fileSize = 0;
- fileIO->Size(filename, fileSize) && fileSize != 0)
- {
- AZStd::string* filenameStore = azcreate(AZStd::string, (filename));
- fileDesc.hFile = AkFileHandle();
- fileDesc.iFileSize = aznumeric_cast<AkInt64>(fileSize);
- fileDesc.uSector = 0;
- fileDesc.deviceID = m_deviceID;
- fileDesc.pCustomParam = filenameStore;
- fileDesc.uCustomParamSize = sizeof(AZStd::string*);
- auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
- streamer->QueueRequest(streamer->CreateDedicatedCache(*filenameStore));
- return true;
- }
- return false;
- }
- AKRESULT CStreamingDevice_wwise::Read(AkFileDesc& fileDesc, const AkIoHeuristics& heuristics, AkAsyncIOTransferInfo& transferInfo)
- {
- AZ_Assert(fileDesc.pCustomParam, "Wwise Async File IO - Reading a file before it has been opened.\n");
- auto callback = [&transferInfo](AZ::IO::FileRequestHandle request)
- {
- AZ_PROFILE_FUNCTION(Audio);
- AZ::IO::IStreamerTypes::RequestStatus status = AZ::Interface<AZ::IO::IStreamer>::Get()->GetRequestStatus(request);
- switch (status)
- {
- case AZ::IO::IStreamerTypes::RequestStatus::Completed:
- transferInfo.pCallback(&transferInfo, AK_Success);
- break;
- case AZ::IO::IStreamerTypes::RequestStatus::Canceled:
- transferInfo.pCallback(&transferInfo, AK_Cancelled);
- break;
- default:
- transferInfo.pCallback(&transferInfo, AK_Fail);
- break;
- }
- };
- // The priorities for Wwise range from 0 (lowest priority) to 100 (highest priority). AZ::IO::Streamer has
- // a similar range except between 0 (lowest) and 255 (highest) so remap from one to the other.
- static_assert(AK_MIN_PRIORITY == 0, "The minimum priority for Wwise has changed, please update the conversion to AZ::IO::Streamers priority.");
- static_assert(AK_DEFAULT_PRIORITY == 50, "The default priority for Wwise has changed, please update the conversion to AZ::IO::Streamers priority.");
- static_assert(AK_MAX_PRIORITY == 100, "The maximum priority for Wwise has changed, please update the conversion to AZ::IO::Streamers priority.");
- static_assert(AZ::IO::IStreamerTypes::s_priorityLowest == 0, "The priority range for AZ::IO::Streamer has changed, please update Wwise to match.");
- static_assert(AZ::IO::IStreamerTypes::s_priorityHighest == 255, "The priority range for AZ::IO::Streamer has changed, please update Wwise to match.");
- AZ::u16 wwisePriority = aznumeric_caster(heuristics.priority);
- AZ::u8 priority = aznumeric_caster(
- (wwisePriority << 1) // 100 -> 200
- + (wwisePriority >> 1) // 200 -> 250
- + (wwisePriority >> 4) // 250 -> 256
- - (wwisePriority >> 6)); // 256 -> 255
- auto filename = reinterpret_cast<AZStd::string*>(fileDesc.pCustomParam);
- auto offset = aznumeric_cast<size_t>(transferInfo.uFilePosition);
- auto readSize = aznumeric_cast<size_t>(transferInfo.uRequestedSize);
- auto bufferSize = aznumeric_cast<size_t>(transferInfo.uBufferSize);
- auto deadline =
- AZStd::chrono::duration_cast<AZ::IO::IStreamerTypes::Deadline>(AZStd::chrono::duration<float, AZStd::milli>(heuristics.fDeadline));
- auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
- AZ::IO::FileRequestPtr request = streamer->Read(*filename, transferInfo.pBuffer, bufferSize, readSize, deadline, priority, offset);
- streamer->SetRequestCompleteCallback(request, AZStd::move(callback));
- streamer->QueueRequest(AZStd::move(request));
- return AK_Success;
- }
- AKRESULT CStreamingDevice_wwise::Write(AkFileDesc&, const AkIoHeuristics&, AkAsyncIOTransferInfo&)
- {
- AZ_Assert(false, "Wwise Async File IO - Writing audio data is not supported for AZ::IO::Streamer based device.\n");
- return AK_Fail;
- }
- AKRESULT CStreamingDevice_wwise::Close(AkFileDesc& fileDesc)
- {
- AZ_Assert(fileDesc.pCustomParam, "Wwise Async File IO - Closing a file before it has been opened.\n");
- auto filename = reinterpret_cast<AZStd::string*>(fileDesc.pCustomParam);
- auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
- streamer->QueueRequest(streamer->DestroyDedicatedCache(*filename));
- azdestroy(filename);
- return AK_Success;
- }
- AkUInt32 CStreamingDevice_wwise::GetBlockSize([[maybe_unused]] AkFileDesc& fileDesc)
- {
- // No constraint on block size (file seeking).
- return 1;
- }
- void CStreamingDevice_wwise::GetDeviceDesc(AkDeviceDesc& deviceDesc)
- {
- deviceDesc.bCanRead = true;
- deviceDesc.bCanWrite = false;
- deviceDesc.deviceID = m_deviceID;
- AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "IO::IStreamer", AZ_ARRAY_SIZE(deviceDesc.szDeviceName));
- deviceDesc.uStringSize = aznumeric_cast<AkUInt32>(AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName));
- }
- AkUInt32 CStreamingDevice_wwise::GetDeviceData()
- {
- return 2;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- CFileIOHandler_wwise::CFileIOHandler_wwise()
- : m_useAsyncOpen(false)
- {
- ::memset(m_bankPath, 0, AK_MAX_PATH * sizeof(AkOSChar));
- ::memset(m_languageFolder, 0, AK_MAX_PATH * sizeof(AkOSChar));
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- AKRESULT CFileIOHandler_wwise::Init(size_t poolSize)
- {
- // If the Stream Manager's File Location Resolver was not set yet, set this object as the
- // File Location Resolver (this I/O hook is also able to resolve file location).
- if (!AK::StreamMgr::GetFileLocationResolver())
- {
- AK::StreamMgr::SetFileLocationResolver(this);
- }
- if (!m_streamingDevice.Init(poolSize))
- {
- return AK_Fail;
- }
- if (!m_blockingDevice.Init(poolSize))
- {
- return AK_Fail;
- }
- return AK_Success;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- void CFileIOHandler_wwise::ShutDown()
- {
- if (AK::StreamMgr::GetFileLocationResolver() == this)
- {
- AK::StreamMgr::SetFileLocationResolver(nullptr);
- }
- m_blockingDevice.Destroy();
- m_streamingDevice.Destroy();
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- AKRESULT CFileIOHandler_wwise::Open(const AkOSChar* fileName, AkOpenMode openMode, AkFileSystemFlags* flags, bool& syncOpen, AkFileDesc& fileDesc)
- {
- AKRESULT akResult = AK_Fail;
- if (syncOpen || !m_useAsyncOpen)
- {
- syncOpen = true;
- AkOSChar finalFilePath[AK_MAX_PATH] = { '\0' };
- AKPLATFORM::SafeStrCat(finalFilePath, m_bankPath, AK_MAX_PATH);
- if (flags && openMode == AK_OpenModeRead)
- {
- // Add language folder if the file is localized.
- if (flags->uCompanyID == AKCOMPANYID_AUDIOKINETIC && flags->uCodecID == AKCODECID_BANK && flags->bIsLanguageSpecific)
- {
- AKPLATFORM::SafeStrCat(finalFilePath, m_languageFolder, AK_MAX_PATH);
- }
- }
- AKPLATFORM::SafeStrCat(finalFilePath, fileName, AK_MAX_PATH);
- char* tempStr = nullptr;
- CONVERT_OSCHAR_TO_CHAR(finalFilePath, tempStr);
- if (openMode == AK_OpenModeRead)
- {
- return m_streamingDevice.Open(tempStr, openMode, fileDesc) ? AK_Success : AK_Fail;
- }
- return m_blockingDevice.Open(tempStr, openMode, fileDesc) ? AK_Success : AK_Fail;
- }
- return akResult;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- AKRESULT CFileIOHandler_wwise::Open(AkFileID fileID, AkOpenMode openMode, AkFileSystemFlags* flags, bool& syncOpen, AkFileDesc& fileDesc)
- {
- AKRESULT akResult = AK_Fail;
- if (flags && (syncOpen || !m_useAsyncOpen))
- {
- syncOpen = true;
- AkOSChar finalFilePath[AK_MAX_PATH] = { '\0' };
- AKPLATFORM::SafeStrCat(finalFilePath, m_bankPath, AK_MAX_PATH);
- if (openMode == AK_OpenModeRead)
- {
- // Add language folder if the file is localized.
- if (flags->uCompanyID == AKCOMPANYID_AUDIOKINETIC && flags->bIsLanguageSpecific)
- {
- AKPLATFORM::SafeStrCat(finalFilePath, m_languageFolder, AK_MAX_PATH);
- }
- }
- AkOSChar fileName[MAX_FILETITLE_SIZE] = { 0 };
- if (flags->uCodecID == AKCODECID_BANK)
- {
- AK_OSPRINTF(fileName, MAX_FILETITLE_SIZE, AKTEXT("%u.bnk"), static_cast<unsigned int>(fileID));
- }
- else
- {
- AK_OSPRINTF(fileName, MAX_FILETITLE_SIZE, AKTEXT("%u.wem"), static_cast<unsigned int>(fileID));
- }
- AKPLATFORM::SafeStrCat(finalFilePath, fileName, AK_MAX_PATH);
- char* filePath = nullptr;
- CONVERT_OSCHAR_TO_CHAR(finalFilePath, filePath);
- if (openMode == AK_OpenModeRead)
- {
- return m_streamingDevice.Open(filePath, openMode, fileDesc) ? AK_Success : AK_Fail;
- }
- return m_blockingDevice.Open(filePath, openMode, fileDesc) ? AK_Success : AK_Fail;
- }
- return akResult;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- void CFileIOHandler_wwise::SetBankPath(const char* const bankPath)
- {
- const AkOSChar* akBankPath = nullptr;
- CONVERT_CHAR_TO_OSCHAR(bankPath, akBankPath);
- AKPLATFORM::SafeStrCpy(m_bankPath, akBankPath, AK_MAX_PATH);
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- void CFileIOHandler_wwise::SetLanguageFolder(const char* const languageFolder)
- {
- const AkOSChar* akLanguageFolder = nullptr;
- CONVERT_CHAR_TO_OSCHAR(languageFolder, akLanguageFolder);
- AKPLATFORM::SafeStrCpy(m_languageFolder, akLanguageFolder, AK_MAX_PATH);
- }
- } // namespace Audio
|